diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /kviewshell/plugins/djvu/libdjvu | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu')
107 files changed, 74443 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/Arrays.cpp b/kviewshell/plugins/djvu/libdjvu/Arrays.cpp new file mode 100644 index 00000000..5cb7b04c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Arrays.cpp @@ -0,0 +1,305 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Arrays.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "Arrays.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int)) : + data(0), minlo(0), maxhi(-1), lobound(0), hibound(-1), + elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ +} + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int hi) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ + resize(0, hi); +} + +ArrayRep::ArrayRep(int xelsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int lo, int hi) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(xelsize), destroy(xdestroy), init1(xinit1), + init2(xinit2), copy(xcopy), insert(xinsert) +{ + resize(lo,hi); +} + +ArrayRep::ArrayRep(const ArrayRep & arr) : data(0), minlo(0), maxhi(-1), + lobound(0), hibound(-1), elsize(arr.elsize), destroy(arr.destroy), + init1(arr.init1), init2(arr.init2), copy(arr.copy), insert(arr.insert) +{ + resize(arr.lobound, arr.hibound); + arr.copy(data, lobound-minlo, hibound-minlo, + arr.data, arr.lobound-arr.minlo, arr.hibound-arr.minlo); +} + +ArrayRep::~ArrayRep() +{ + destroy(data, lobound-minlo, hibound-minlo); + operator delete(data); + data=0; +} + +ArrayRep & +ArrayRep::operator= (const ArrayRep & rep) +{ + if (&rep == this) return *this; + empty(); + resize(rep.lobound, rep.hibound); + copy(data, lobound-minlo, hibound-minlo, + rep.data, rep.lobound-rep.minlo, rep.hibound-rep.minlo); + return *this; +} + +void +ArrayRep::resize(int lo, int hi) +{ + int nsize = hi - lo + 1; + // Validation + if (nsize < 0) + G_THROW( ERR_MSG("arrays.resize") ); + // Destruction + if (nsize == 0) + { + destroy(data, lobound-minlo, hibound-minlo); + operator delete(data); + data = 0; + lobound = minlo = lo; + hibound = maxhi = hi; + return; + } + // Simple extension + if (lo >= minlo && hi <= maxhi) + { + init1(data, lo-minlo, lobound-1-minlo); + destroy(data, lobound-minlo, lo-1-minlo); + init1(data, hibound+1-minlo, hi-minlo); + destroy(data, hi+1-minlo, hibound-minlo); + lobound = lo; + hibound = hi; + return; + } + // General case + int nminlo = minlo; + int nmaxhi = maxhi; + if (nminlo > nmaxhi) + nminlo = nmaxhi = lo; + while (nminlo > lo) { + int incr = nmaxhi - nminlo; + nminlo -= (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + while (nmaxhi < hi) { + int incr = nmaxhi - nminlo; + nmaxhi += (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + // allocate + int bytesize=elsize*(nmaxhi-nminlo+1); + void * ndata; + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); + // initialize + init1(ndata, lo-nminlo, lobound-1-nminlo); + init2(ndata, lobound-nminlo, hibound-nminlo, + data, lobound-minlo, hibound-minlo); + init1(ndata, hibound+1-nminlo, hi-nminlo); + destroy(data, lobound-minlo, hibound-minlo); + + // free and replace + void *tmp=data; + data = ndata; + ndata=tmp; + + minlo = nminlo; + maxhi = nmaxhi; + lobound = lo; + hibound = hi; +} + +void +ArrayRep::shift(int disp) +{ + lobound += disp; + hibound += disp; + minlo += disp; + maxhi += disp; +} + +void +ArrayRep::del(int n, unsigned int howmany) +{ + if (howmany == 0) + return; + if ((int)(n + howmany) > hibound +1) + G_THROW( ERR_MSG("arrays.ill_arg") ); + copy(data, n-minlo, hibound-howmany-minlo, + data, n+howmany-minlo, hibound-minlo); + destroy(data, hibound+1-howmany-minlo, hibound-minlo); + hibound = hibound - howmany; +} + +void +ArrayRep::ins(int n, const void * what, unsigned int howmany) +{ + int nhi = hibound + howmany; + if (howmany == 0) return; + if (maxhi < nhi) + { + int nmaxhi = maxhi; + while (nmaxhi < nhi) + nmaxhi += (nmaxhi < 8 ? 8 : (nmaxhi > 32768 ? 32768 : nmaxhi)); + int bytesize = elsize*(nmaxhi-minlo+1); + void *ndata; + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); + copy(ndata, lobound-minlo, hibound-minlo, + data, lobound-minlo, hibound-minlo); + destroy(data, lobound-minlo, hibound-minlo); + void *tmp=data; + data=ndata; + tmp=data; + maxhi = nmaxhi; + } + + insert(data, hibound+1-minlo, n-minlo, what, howmany); + hibound=nhi; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + + +// --------------------------------------- +// BEGIN HACK +// --------------------------------------- +// Included here to avoid dependency +// from ByteStream.o to Arrays.o + +#ifndef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP +#include "ByteStream.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif +TArray<char> +ByteStream::get_data(void) +{ + const int s=size(); + if(s > 0) + { + TArray<char> data(0, s-1); + readat((char*)data, s, 0); + return data; + }else + { + TArray<char> data(0, -1); + return data; + } +} + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + +// --------------------------------------- +// END HACK +// --------------------------------------- + diff --git a/kviewshell/plugins/djvu/libdjvu/Arrays.h b/kviewshell/plugins/djvu/libdjvu/Arrays.h new file mode 100644 index 00000000..b2676d5a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Arrays.h @@ -0,0 +1,997 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Arrays.h,v 1.10 2004/05/13 15:16:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _ARRAYS_H_ +#define _ARRAYS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "GException.h" +#include "GSmartPointer.h" +#include <string.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** @name Arrays.h + + Files #"Arrays.h"# and #"Arrays.cpp"# implement three array template classes. + Class \Ref{TArray} implements an array of objects of trivial types + such as #char#, #int#, #float#, etc. It is faster than general implementation + for any type done in \Ref{DArray} because it does not cope with + element's constructors, destructors and copy operators. Although + implemented as a template, which makes it possible to incorrectly use + \Ref{TArray} with non-trivial classes, it should not be done. + + A lot of things is shared by these three arrays. That is why there are + more base classes: + \begin{itemize} + \item \Ref{ArrayBase} defines functions independent of the elements type + \item \Ref{ArrayBaseT} template class defining functions shared by + \Ref{DArray} and \Ref{TArray} + \end{itemize} + + The main difference between \Ref{GArray} (now obsolete) and these ones + is the copy-on-demand strategy, which allows you to copy array objects + without copying the real data. It's the same thing, which has been + implemented in \Ref{GString} long ago: as long as you don't try to modify + the underlying data, it may be shared between several copies of array + objects. As soon as you attempt to make any changes, a private copy + is created automatically and transparently for you - the procedure, that + we call "copy-on-demand". + + Also, please note that now there is no separate class, which does fast + sorting. Both \Ref{TArray} (dynamic array for trivial types) and + \Ref{DArray} (dynamic array for arbitrary types) can sort their elements. + + {\bf Historical comments} --- Leon chose to implement his own arrays because + the STL classes were not universally available and the compilers were + rarely able to deal with such a template galore. Later it became clear + that there is no really good reason why arrays should be derived from + containers. It was also suggested to create separate arrays implementation + for simple classes and do the copy-on-demand strategy, which would allow + to assign array objects without immediate copying of their elements. + + At this point \Ref{DArray} and \Ref{TArray} should only be used when + it is critical to have the copy-on-demand feature. The \Ref{GArray} + implementation is a lot more efficient. + + @memo Template array classes. + @author + Andrei Erofeev <[email protected]> -- Copy-on-demand implementation. + @version + #$Id: Arrays.h,v 1.10 2004/05/13 15:16:34 leonb Exp $# */ +//@{ + +// Auxiliary classes: Will be used in place of GPBase and GPEnabled objects +class _ArrayRep +{ + friend class _ArrayBase; +public: + _ArrayRep(void) : count(0) {} + _ArrayRep(const _ArrayRep &) {} + virtual ~_ArrayRep(void) {} + + _ArrayRep & operator=(const _ArrayRep &) { return *this; } + + int get_count(void) const { return count; } +private: + int count; + + void ref(void) { count++; } + void unref(void) { if (--count==0) delete this; } +}; + +class _ArrayBase +{ +public: + _ArrayBase(void) : rep(0) {} + _ArrayBase(const _ArrayBase & ab) : rep(0) + { + if (ab.rep) ab.rep->ref(); + rep=ab.rep; + } + _ArrayBase(_ArrayRep * ar) : rep(0) + { + if (ar) ar->ref(); + rep=ar; + } + virtual ~_ArrayBase(void) + { + if (rep) { rep->unref(); rep=0; } + } + + _ArrayRep * get(void) const { return rep; } + _ArrayBase & assign(_ArrayRep * ar) + { + if (ar) ar->ref(); + if (rep) rep->unref(); + rep=ar; + return *this; + } + _ArrayBase & operator=(const _ArrayBase & ab) { return assign(ab.rep); } + bool operator==(const _ArrayBase & ab) { return rep==ab.rep; } +private: + _ArrayRep * rep; +}; + +// Internal "Array repository" holding the pointer to the actual data, +// data bounds, etc. It copes with data elements with the help of five +// static functions which pointers are supposed to be passed to the +// constructor. +class ArrayRep : public _ArrayRep +{ +public: + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int)); + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int hibound); + ArrayRep(int elsize, + void (* xdestroy)(void *, int, int), + void (* xinit1)(void *, int, int), + void (* xinit2)(void *, int, int, const void *, int, int), + void (* xcopy)(void *, int, int, const void *, int, int), + void (* xinsert)(void *, int, int, const void *, int), + int lobound, int hibound); + ArrayRep(const ArrayRep & rep); + + virtual ~ArrayRep(); + + // Following is the standard interface to DArray. DArray will call these + // functions to access data. + int size() const; + int lbound() const; + int hbound() const; + + void empty(); + void touch(int n); + void resize(int lobound, int hibound); + void shift(int disp); + void del(int n, unsigned int howmany=1); + + // ins() is an exception. It does it job only partially. + // The derived class is supposed to finish insertion. + void ins(int n, const void * what, unsigned int howmany); + + ArrayRep & operator=(const ArrayRep & rep); + + // All data is public because DArray... classes will need access to it + void *data; + int minlo; + int maxhi; + int lobound; + int hibound; + int elsize; +private: + // These functions can't be virtual as they're called from + // constructors and destructors :(( + // destroy(): should destroy elements in data[] array from 'lo' to 'hi' + void (* destroy)(void * data, int lo, int hi); + // init1(): should initialize elements in data[] from 'lo' to 'hi' + // using default constructors + void (* init1)(void * data, int lo, int hi); + // init2(): should initialize elements in data[] from 'lo' to 'hi' + // using corresponding elements from src[] (copy constructor) + void (* init2)(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + // copy(): should copy elements from src[] to dst[] (copy operator) + void (* copy)(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + // insert(): should insert '*what' at position 'where' 'howmany' times + // into array data[] having 'els' initialized elements + void (* insert)(void * data, int els, int where, const void * what, + int howmany); +}; + +inline int +ArrayRep::size() const +{ + return hibound - lobound + 1; +} + +inline int +ArrayRep::lbound() const +{ + return lobound; +} + +inline int +ArrayRep::hbound() const +{ + return hibound; +} + +inline void +ArrayRep::empty() +{ + resize(0, -1); +} + +inline void +ArrayRep::touch(int n) +{ + if (hibound < lobound) + { + resize(n,n); + } else + { + int nlo = lobound; + int nhi = hibound; + if (n < nlo) nlo = n; + if (n > nhi) nhi = n; + resize(nlo, nhi); + } +} + +/** Dynamic array base class. + This is an auxiliary base class for \Ref{DArray} and \Ref{TArray} + implementing some shared functions independent of the type of array + elements. It's not supposed to be constructed by hands. Use \Ref{DArray} + and \Ref{TArray} instead. + */ + +class ArrayBase : protected _ArrayBase +{ +protected: + void check(void); + void detach(void); + + ArrayBase(void) {}; +public: + /// Returns the number of elements in the array + int size() const; + /** Returns the lower bound of the valid subscript range. */ + int lbound() const; + /** Returns the upper bound of the valid subscript range. */ + int hbound() const; + /** Erases the array contents. All elements in the array are destroyed. + The valid subscript range is set to the empty range. */ + void empty(); + /** Extends the subscript range so that is contains #n#. + This function does nothing if #n# is already int the valid subscript range. + If the valid range was empty, both the lower bound and the upper bound + are set to #n#. Otherwise the valid subscript range is extended + to encompass #n#. This function is very handy when called before setting + an array element: + \begin{verbatim} + int lineno=1; + DArray<GString> a; + while (! end_of_file()) { + a.touch[lineno]; + a[lineno++] = read_a_line(); + } + \end{verbatim} + */ + void touch(int n); + /** Resets the valid subscript range to #0#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #hibound# to + #-1# resets the valid subscript range to the empty range. + @param hibound upper bound of the new subscript range. */ + void resize(int hibound); + /** Resets the valid subscript range to #lobound#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #lobound# to #0# and + #hibound# to #-1# resets the valid subscript range to the empty range. + @param lobound lower bound of the new subscript range. + @param hibound upper bound of the new subscript range. */ + void resize(int lobound, int hibound); + /** Shifts the valid subscript range. Argument #disp# is added to both + bounds of the valid subscript range. Array elements previously + located at subscript #x# will now be located at subscript #x+disp#. */ + void shift(int disp); + /** Deletes array elements. The array elements corresponding to + subscripts #n#...#n+howmany-1# are destroyed. All array elements + previously located at subscripts greater or equal to #n+howmany# + are moved to subscripts starting with #n#. The new subscript upper + bound is reduced in order to account for this shift. + @param n subscript of the first element to delete. + @param howmany number of elements to delete. */ + void del(int n, unsigned int howmany=1); + + virtual ~ArrayBase(void) {}; +}; + +inline void +ArrayBase::detach(void) +{ + ArrayRep * new_rep=new ArrayRep(*(ArrayRep *) get()); + assign(new_rep); +} + +inline void +ArrayBase::check(void) +{ + if (get()->get_count()>1) detach(); +} + +inline int +ArrayBase::size() const +{ + return ((const ArrayRep *) get())->size(); +} + +inline int +ArrayBase::lbound() const +{ + return ((const ArrayRep *) get())->lobound; +} + +inline int +ArrayBase::hbound() const +{ + return ((const ArrayRep *) get())->hibound; +} + +inline void +ArrayBase::empty() +{ + check(); + ((ArrayRep *) get())->empty(); +} + +inline void +ArrayBase::resize(int lo, int hi) +{ + check(); + ((ArrayRep *) get())->resize(lo, hi); +} + +inline void +ArrayBase::resize(int hi) +{ + resize(0, hi); +} + +inline void +ArrayBase::touch(int n) +{ + check(); + ((ArrayRep *) get())->touch(n); +} + +inline void +ArrayBase::shift(int disp) +{ + check(); + ((ArrayRep *) get())->shift(disp); +} + +inline void +ArrayBase::del(int n, unsigned int howmany) +{ + check(); + + ((ArrayRep *) get())->del(n, howmany); +} + +/** Dynamic array template base class. + This is an auxiliary template base class for \Ref{DArray} and \Ref{TArray} + implementing some shared functions which {\em depend} on the type of + the array elements (this is contrary to \Ref{ArrayBase}). + It's not supposed to be constructed by hands. Use \Ref{DArray} and + \Ref{TArray} instead. + */ + +template <class TYPE> +class ArrayBaseT : public ArrayBase +{ +public: + virtual ~ArrayBaseT(void) {}; + + /** Returns a reference to the array element for subscript #n#. This + reference can be used for both reading (as "#a[n]#") and writing (as + "#a[n]=v#") an array element. This operation will not extend the valid + subscript range: an exception \Ref{GException} is thrown if argument #n# + is not in the valid subscript range. */ + TYPE& operator[](int n); + /** Returns a constant reference to the array element for subscript #n#. + This reference can only be used for reading (as "#a[n]#") an array + element. This operation will not extend the valid subscript range: an + exception \Ref{GException} is thrown if argument #n# is not in the valid + subscript range. This variant of #operator[]# is necessary when dealing + with a #const DArray<TYPE>#. */ + const TYPE& operator[](int n) const; + + /** Returns a pointer for reading or writing the array elements. This + pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator TYPE* (); + /** Returns a pointer for reading (but not modifying) the array elements. + This pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator const TYPE* () const; + +#ifndef __MWERKS__ //MCW can't compile + operator const TYPE* (); +#endif + /** Insert new elements into an array. This function inserts + #howmany# elements at position #n# into the array. The initial value #val# + is copied into the new elements. All array elements previously located at subscripts + #n# and higher are moved to subscripts #n+howmany# and higher. The upper bound of the + valid subscript range is increased in order to account for this shift. + @param n subscript of the first inserted element. + @param val initial value of the new elements. + @param howmany number of elements to insert. */ + void ins(int n, const TYPE &val, unsigned int howmany=1); + + /** Sort array elements. Sort all array elements in ascending order. Array + elements are compared using the less-or-equal comparison operator for + type #TYPE#. */ + void sort(); + /** Sort array elements in subscript range #lo# to #hi#. Sort all array + elements whose subscripts are in range #lo#..#hi# in ascending order. + The other elements of the array are left untouched. An exception is + thrown if arguments #lo# and #hi# are not in the valid subscript range. + Array elements are compared using the less-or-equal comparison operator + for type #TYPE#. + @param lo low bound for the subscripts of the elements to sort. + @param hi high bound for the subscripts of the elements to sort. */ + void sort(int lo, int hi); +protected: + ArrayBaseT(void) {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> inline +ArrayBaseT<TYPE>::operator TYPE* () +{ + check(); + + ArrayRep * rep=(ArrayRep *) get(); + return &((TYPE *) rep->data)[-rep->minlo]; +} + +#ifndef __MWERKS__ //MCW can't compile +template <class TYPE> inline +ArrayBaseT<TYPE>::operator const TYPE* () +{ + const ArrayRep * rep=(const ArrayRep *) get(); + return &((const TYPE *) rep->data)[-rep->minlo]; +} +#endif + +template <class TYPE> inline +ArrayBaseT<TYPE>::operator const TYPE* () const +{ + const ArrayRep * rep=(const ArrayRep *) get(); + return &((const TYPE *) rep->data)[-rep->minlo]; +} + +template <class TYPE> inline TYPE& +ArrayBaseT<TYPE>::operator[](int n) +{ + check(); + + ArrayRep * rep=(ArrayRep *) get(); + if (n<rep->lobound || n>rep->hibound) + G_THROW( ERR_MSG("arrays.ill_sub") ); + return ((TYPE *) rep->data)[n - rep->minlo]; +} + +template <class TYPE> inline const TYPE& +ArrayBaseT<TYPE>::operator[](int n) const +{ + const ArrayRep * rep=(const ArrayRep *) get(); + if (n<rep->lobound || n>rep->hibound) + G_THROW( ERR_MSG("arrays.ill_sub") ); + return ((const TYPE *) rep->data)[n - rep->minlo]; +} + +template <class TYPE> inline void +ArrayBaseT<TYPE>::ins(int n, const TYPE &val, unsigned int howmany) +{ + check(); + + ((ArrayRep *) get())->ins(n, &val, howmany); +} + +template <class TYPE> void +ArrayBaseT<TYPE>::sort() +{ + sort(lbound(), hbound()); +} + +template <class TYPE> void +ArrayBaseT<TYPE>::sort(int lo, int hi) +{ + if (hi <= lo) + return; + // Test for insertion sort (optimize!) + if (hi <= lo + 20) + { + for (int i=lo+1; i<=hi; i++) + { + int j = i; + TYPE tmp = (*this)[i]; + while ((--j>=lo) && !((*this)[j]<=tmp)) + (*this)[j+1] = (*this)[j]; + (*this)[j+1] = tmp; + } + return; + } + // -- determine suitable quick-sort pivot + TYPE tmp = (*this)[lo]; + TYPE pivot = (*this)[(lo+hi)/2]; + if (pivot <= tmp) + { tmp = pivot; pivot=(*this)[lo]; } + if ((*this)[hi] <= tmp) + { pivot = tmp; } + else if ((*this)[hi] <= pivot) + { pivot = (*this)[hi]; } + // -- partition set + int h = hi; + int l = lo; + while (l < h) + { + while (! (pivot <= (*this)[l])) l++; + while (! ((*this)[h] <= pivot)) h--; + if (l < h) + { + tmp = (*this)[l]; + (*this)[l] = (*this)[h]; + (*this)[h] = tmp; + l = l+1; + h = h-1; + } + } + // -- recursively restart + sort(lo, h); + sort(l, hi); +} + +/** Dynamic array for simple types. + Template class #TArray<TYPE># implements an array of + elements of {\em simple} type #TYPE#. {\em Simple} means that the type + may be #char#, #int#, #float# etc. The limitation is imposed by the + way in which the #TArray# is working with its elements: it's not trying + to execute elements' constructors, destructors or copy operators. It's + just doing bitwise copy. Except for this it's pretty much the same as + \Ref{DArray}. + + Please note that most of the methods are implemented in the base classes + \Ref{ArrayBase} and \Ref{ArrayBaseT}. +*/ + +template <class TYPE> +class TArray : public ArrayBaseT<TYPE> { +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + TArray(); + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param hibound upper bound of the initial subscript range. */ + TArray(int hibound); + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param lobound lower bound of the initial subscript range. + @param hibound upper bound of the initial subscript range. */ + TArray(int lobound, int hibound); + + virtual ~TArray() {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> void +TArray<TYPE>::destroy(void * data, int lo, int hi) +{ +} + +template <class TYPE> void +TArray<TYPE>::init1(void * data, int lo, int hi) +{ +} + +template <class TYPE> void +TArray<TYPE>::init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi) +{ + if (data && src) + { + int els=hi-lo+1; + if (els>src_hi-src_lo+1) els=src_hi-src_lo+1; + if (els>0) + memmove((void *) &((TYPE *) data)[lo], + (void *) &((TYPE *) src)[src_lo], els*sizeof(TYPE)); + }; +} + +// inline removed +template <class TYPE> void +TArray<TYPE>::insert(void * data, int els, int where, + const void * what, int howmany) +{ + memmove(((TYPE *) data)+where+howmany, + ((TYPE *) data)+where, sizeof(TYPE)*(els-where)); + for(int i=0;i<howmany;i++) + ((TYPE *) data)[where+i]=*(TYPE *) what; +} + +template <class TYPE> +TArray<TYPE>::TArray () +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert)); +} + +template <class TYPE> +TArray<TYPE>::TArray(int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert, hi)); +} + +template <class TYPE> +TArray<TYPE>::TArray(int lo, int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, init2, insert, lo, hi)); +} + +//inline removal ends + +/** Dynamic array for general types. + Template class #DArray<TYPE># implements an array of + elements of type #TYPE#. Each element is identified by an integer + subscript. The valid subscripts range is defined by dynamically + adjustable lower- and upper-bounds. Besides accessing and setting + elements, member functions are provided to insert or delete elements at + specified positions. + + This template class must be able to access + \begin{itemize} + \item a null constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item and a copy operator #TYPE & operator=(const TYPE &)#. + \end{itemize} + + The class offers "copy-on-demand" policy, which means that when you + copy the array object, array elements will stay intact as long as you + don't try to modify them. As soon as you make an attempt to change + array contents, the copying is done automatically and transparently + for you - the procedure that we call "copy-on-demand". This is the main + difference between this class and \Ref{GArray} (now obsolete) + + Please note that most of the methods are implemented in the base classes + \Ref{ArrayBase} and \Ref{ArrayBaseT}. +*/ + +template <class TYPE> +class DArray : public ArrayBaseT<TYPE> { +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + DArray(void); + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param hibound upper bound of the initial subscript range. */ + DArray(const int hibound); + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. + @param lobound lower bound of the initial subscript range. + @param hibound upper bound of the initial subscript range. */ + DArray(const int lobound, const int hibound); + + virtual ~DArray() {}; +private: + // Callbacks called from ArrayRep + static void destroy(void * data, int lo, int hi); + static void init1(void * data, int lo, int hi); + static void init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi); + static void copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi); + static void insert(void * data, int els, int where, + const void * what, int howmany); +}; + +template <class TYPE> void +DArray<TYPE>::destroy(void * data, int lo, int hi) +{ + if (data) + for(int i=lo;i<=hi;i++) + ((TYPE *) data)[i].TYPE::~TYPE(); +} + +template <class TYPE> void +DArray<TYPE>::init1(void * data, int lo, int hi) +{ + if (data) + for(int i=lo;i<=hi;i++) + new ((void *) &((TYPE *) data)[i]) TYPE; +} + +template <class TYPE> void +DArray<TYPE>::init2(void * data, int lo, int hi, + const void * src, int src_lo, int src_hi) +{ + if (data && src) + { + int i, j; + for(i=lo, j=src_lo;i<=hi && j<=src_hi;i++, j++) + new ((void *) &((TYPE *) data)[i]) TYPE(((TYPE *) src)[j]); + }; +} + +template <class TYPE> void +DArray<TYPE>::copy(void * dst, int dst_lo, int dst_hi, + const void * src, int src_lo, int src_hi) +{ + if (dst && src) + { + int i, j; + for(i=dst_lo, j=src_lo;i<=dst_hi && j<=src_hi;i++, j++) + ((TYPE *) dst)[i]=((TYPE *) src)[j]; + }; +} + +template <class TYPE> inline void +DArray<TYPE>::insert(void * data, int els, int where, + const void * what, int howmany) +{ + // Now do the insertion + TYPE * d=(TYPE *) data; + + int i; + for (i=els+howmany-1; i>=els; i--) + { + if (i-where >= (int)howmany) + new ((void*) &d[i]) TYPE (d[i-howmany]); + else + new ((void*) &d[i]) TYPE (*(TYPE *) what); + } + + for (i=els-1; i>=where; i--) + { + if (i-where >= (int)howmany) + d[i] = d[i-howmany]; + else + d[i] = *(TYPE *) what; + } +} + +template <class TYPE> inline +DArray<TYPE>::DArray () +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert)); +} + +template <class TYPE> inline +DArray<TYPE>::DArray(const int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert, hi)); +} + +template <class TYPE> inline +DArray<TYPE>::DArray(const int lo, const int hi) +{ + this->assign(new ArrayRep(sizeof(TYPE), destroy, init1, + init2, copy, insert, lo, hi)); +} + +/** Dynamic array for \Ref{GPBase}d classes. + + There are many situations when it's necessary to create arrays of + \Ref{GP} pointers. For example, #DArray<GP<Dialog> ># or #DArray<GP<Button> >#. + This would result in compilation of two instances of \Ref{DArray} because + from the viewpoint of the compiler there are two different classes used + as array elements: #GP<Dialog># and #GP<Button>#. In reality though, + all \Ref{GP} pointers have absolutely the same binary structure because + they are derived from \Ref{GPBase} class and do not add any variables + or virtual functions. That's why it's possible to instantiate \Ref{DArray} + only once for \Ref{GPBase} elements and then just cast types. + + To implement this idea we have created this #DPArray<TYPE># class, + which can be used instead of #DArray<GP<TYPE> >#. It behaves absolutely + the same way as \Ref{DArray} but has one big advantage: overhead of + using #DPArray# with one more type is negligible. + */ +template <class TYPE> +class DPArray : public DArray<GPBase> { +public: + // -- CONSTRUCTORS + DPArray(); + DPArray(int hibound); + DPArray(int lobound, int hibound); + DPArray(const DPArray<TYPE> &gc); + // -- DESTRUCTOR + virtual ~DPArray(); + // -- ACCESS + GP<TYPE>& operator[](int n); + const GP<TYPE>& operator[](int n) const; + // -- CONVERSION + operator GP<TYPE>* (); + +#ifndef __MWERKS__ //MCW can't compile + operator const GP<TYPE>* (); +#endif + + operator const GP<TYPE>* () const; + // -- ALTERATION + void ins(int n, const GP<TYPE> &val, unsigned int howmany=1); + DPArray<TYPE>& operator= (const DPArray &ga); +}; + +template<class TYPE> +DPArray<TYPE>::DPArray() {} + +template<class TYPE> +DPArray<TYPE>::DPArray(int hibound) : + DArray<GPBase>(hibound) {} + +template<class TYPE> +DPArray<TYPE>::DPArray(int lobound, int hibound) : + DArray<GPBase>(lobound, hibound) {} + +template<class TYPE> +DPArray<TYPE>::DPArray(const DPArray<TYPE> &gc) : + DArray<GPBase>(gc) {} + +template<class TYPE> +DPArray<TYPE>::~DPArray() {} + +template<class TYPE> +inline GP<TYPE> & +DPArray<TYPE>::operator[](int n) +{ + return (GP<TYPE> &) DArray<GPBase>::operator[](n); +} + +template<class TYPE> +inline const GP<TYPE> & +DPArray<TYPE>::operator[](int n) const +{ + return (const GP<TYPE> &) DArray<GPBase>::operator[](n); +} + +template<class TYPE> +inline DPArray<TYPE>::operator GP<TYPE>* () +{ + return (GP<TYPE> *) DArray<GPBase>::operator GPBase*(); +} + +#ifndef __MWERKS__ //MCW can't compile +template<class TYPE> +inline DPArray<TYPE>::operator const GP<TYPE>* () +{ + return (const GP<TYPE> *) DArray<GPBase>::operator const GPBase*(); +} +#endif + +template<class TYPE> +inline DPArray<TYPE>::operator const GP<TYPE>* () const +{ + return (const GP<TYPE> *) DArray<GPBase>::operator const GPBase*(); +} + +template<class TYPE> +inline void +DPArray<TYPE>::ins(int n, const GP<TYPE> & val, unsigned int howmany) +{ + DArray<GPBase>::ins(n, val, howmany); +} + +template<class TYPE> +inline DPArray<TYPE> & +DPArray<TYPE>::operator= (const DPArray &ga) +{ + DArray<GPBase>::operator=(ga); + return *this; +} + +// ------------ THE END + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp new file mode 100644 index 00000000..77334a45 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp @@ -0,0 +1,465 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSByteStream.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 07/1998 + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "BSByteStream.h" +#undef BSORT_TIMER +#ifdef BSORT_TIMER +#include "GOS.h" +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class BSByteStream::Decode : public BSByteStream +{ +public: + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Decode(GP<ByteStream> bs); + ~Decode(); + void init(void); + // Virtual functions + virtual size_t read(void *buffer, size_t sz); + virtual void flush(void); +protected: + unsigned int decode(void); +private: + bool eof; +}; + +// ======================================== +// --- Assertion + +#define ASSERT(expr) do{if(!(expr))G_THROW("assertion ("#expr") failed");}while(0) + +// ======================================== +// --- Construction + +BSByteStream::BSByteStream(GP<ByteStream> xbs) +: offset(0), bptr(0), blocksize(0), size(0), bs(xbs), + gbs(xbs), gdata(data,0) +{ + // Initialize context array + memset(ctx, 0, sizeof(ctx)); +} + +BSByteStream::~BSByteStream() {} + +BSByteStream::Decode::Decode(GP<ByteStream> xbs) +: BSByteStream(xbs), eof(false) {} + +void +BSByteStream::Decode::init(void) +{ + gzp=ZPCodec::create(gbs,false,true); +} + +BSByteStream::Decode::~Decode() {} + +GP<ByteStream> +BSByteStream::create(GP<ByteStream> xbs) +{ + BSByteStream::Decode *rbs=new BSByteStream::Decode(xbs); + GP<ByteStream> retval=rbs; + rbs->init(); + return retval; +} + +void +BSByteStream::Decode::flush() +{ + size = bptr = 0; +} + +// ======================================== +// -- Decoding + + +static int +decode_raw(ZPCodec &zp, int bits) +{ + int n = 1; + const int m = (1<<bits); + while (n < m) + { + const int b = zp.decoder(); + n = (n<<1) | b; + } + return n - m; +} + +static inline int +decode_binary(ZPCodec &zp, BitContext *ctx, int bits) +{ + int n = 1; + int m = (1<<bits); + ctx = ctx - 1; + while (n < m) + { + int b = zp.decoder(ctx[n]); + n = (n<<1) | b; + } + return n - m; +} + + +static inline void +assignmtf(unsigned char xmtf[256]) +{ + static const unsigned char mtf[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, + 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, + 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, + 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, + 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, + 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, + 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, + 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, + 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, + 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + memcpy(xmtf,mtf,sizeof(mtf)); +} + +unsigned int +BSByteStream::Decode::decode(void) +{ + ///////////////////////////////// + //////////// Decode input stream + + int i; + // Decode block size + ZPCodec &zp=*gzp; + size = decode_raw(zp, 24); + if (!size) + return 0; + if (size>MAXBLOCK*1024) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + // Allocate + if ((int)blocksize < size) + { + blocksize = size; + if (data) + { + gdata.resize(0); + } + } + if (! data) + gdata.resize(blocksize); + // Decode Estimation Speed + int fshift = 0; + if (zp.decoder()) + { + fshift += 1; + if (zp.decoder()) + fshift += 1; + } + // Prepare Quasi MTF + static const unsigned char xmtf[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, + 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, + 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, + 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, + 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, + 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, + 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, + 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, + 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, + 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, + 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + unsigned char mtf[256]; + memcpy(mtf,xmtf,sizeof(xmtf)); + unsigned int freq[FREQMAX]; + memset(freq,0,sizeof(freq)); + int fadd = 4; + // Decode + int mtfno = 3; + int markerpos = -1; + for (i=0; i<size; i++) + { + int ctxid = CTXIDS-1; + if (ctxid>mtfno) ctxid=mtfno; + BitContext *cx = ctx; + if (zp.decoder(cx[ctxid])) + { mtfno=0; data[i]=mtf[mtfno]; goto rotate; } + cx+=CTXIDS; + if (zp.decoder(cx[ctxid])) + { mtfno=1; data[i]=mtf[mtfno]; goto rotate; } + cx+=CTXIDS; + if (zp.decoder(cx[0])) + { mtfno=2+decode_binary(zp,cx+1,1); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+1; + if (zp.decoder(cx[0])) + { mtfno=4+decode_binary(zp,cx+1,2); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+3; + if (zp.decoder(cx[0])) + { mtfno=8+decode_binary(zp,cx+1,3); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+7; + if (zp.decoder(cx[0])) + { mtfno=16+decode_binary(zp,cx+1,4); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+15; + if (zp.decoder(cx[0])) + { mtfno=32+decode_binary(zp,cx+1,5); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+31; + if (zp.decoder(cx[0])) + { mtfno=64+decode_binary(zp,cx+1,6); data[i]=mtf[mtfno]; goto rotate; } + cx+=1+63; + if (zp.decoder(cx[0])) + { mtfno=128+decode_binary(zp,cx+1,7); data[i]=mtf[mtfno]; goto rotate; } + mtfno=256; + data[i]=0; + markerpos=i; + continue; + // Rotate mtf according to empirical frequencies (new!) + rotate: + // Adjust frequencies for overflow + int k; + fadd = fadd + (fadd>>fshift); + if (fadd > 0x10000000) + { + fadd >>= 24; + freq[0] >>= 24; + freq[1] >>= 24; + freq[2] >>= 24; + freq[3] >>= 24; + for (k=4; k<FREQMAX; k++) + freq[k] = freq[k]>>24; + } + // Relocate new char according to new freq + unsigned int fc = fadd; + if (mtfno < FREQMAX) + fc += freq[mtfno]; + for (k=mtfno; k>=FREQMAX; k--) + mtf[k] = mtf[k-1]; + for (; k>0 && fc>=freq[k-1]; k--) + { + mtf[k] = mtf[k-1]; + freq[k] = freq[k-1]; + } + mtf[k] = data[i]; + freq[k] = fc; + } + + + ///////////////////////////////// + ////////// Reconstruct the string + + if (markerpos<1 || markerpos>=size) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + // Allocate pointers + unsigned int *posn; + GPBuffer<unsigned int> gposn(posn,blocksize); + memset(posn, 0, sizeof(unsigned int)*size); + // Prepare count buffer + int count[256]; + for (i=0; i<256; i++) + count[i] = 0; + // Fill count buffer + for (i=0; i<markerpos; i++) + { + unsigned char c = data[i]; + posn[i] = (c<<24) | (count[c] & 0xffffff); + count[c] += 1; + } + for (i=markerpos+1; i<size; i++) + { + unsigned char c = data[i]; + posn[i] = (c<<24) | (count[c] & 0xffffff); + count[c] += 1; + } + // Compute sorted char positions + int last = 1; + for (i=0; i<256; i++) + { + int tmp = count[i]; + count[i] = last; + last += tmp; + } + // Undo the sort transform + i = 0; + last = size-1; + while (last>0) + { + unsigned int n = posn[i]; + unsigned char c = (posn[i]>>24); + data[--last] = c; + i = count[c] + (n & 0xffffff); + } + // Free and check + if (i != markerpos) + G_THROW( ERR_MSG("ByteStream.corrupt") ); + return size; +} + + + +// ======================================== +// -- ByteStream interface + + + +long +BSByteStream::tell() const +{ + return offset; +} + +size_t +BSByteStream::Decode::read(void *buffer, size_t sz) +{ + if (eof) + return 0; + // Loop + int copied = 0; + while (sz>0 && !eof) + { + // Decode if needed + if (!size) + { + bptr = 0; + if (! decode()) + { + size = 1 ; + eof = true; + } + size -= 1; + } + // Compute remaining + int bytes = size; + if (bytes > (int)sz) + bytes = sz; + // Transfer + if (buffer && bytes) + { + memcpy(buffer, data+bptr, bytes); + buffer = (void*)((char*)buffer + bytes); + } + size -= bytes; + bptr += bytes; + sz -= bytes; + copied += bytes; + offset += bytes; + } + // Return copied bytes + return copied; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/BSByteStream.h b/kviewshell/plugins/djvu/libdjvu/BSByteStream.h new file mode 100644 index 00000000..6a985cdf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSByteStream.h @@ -0,0 +1,275 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSByteStream.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _BSBYTESTREAM_H +#define _BSBYTESTREAM_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name BSByteStream.h + + Files #"BSByteStream.h"# and #"BSByteStream.cpp"# implement a very compact + general purpose compressor based on the Burrows-Wheeler transform. The + utility program \Ref{bzz} provides a front-end for this class. Although + this compression model is not currently used in DjVu files, it may be used + in the future for encoding textual data chunks. + + {\bf Algorithms} --- The Burrows-Wheeler transform (also named Block-Sorting) + is performed using a combination of the Karp-Miller-Rosenberg and the + Bentley-Sedgewick algorithms. This is comparable to (Sadakane, DCC 98) + with a slightly more flexible ranking scheme. Symbols are then ordered + according to a running estimate of their occurrence frequencies. The + symbol ranks are then coded using a simple fixed tree and the + \Ref{ZPCodec} binary adaptive coder. + + {\bf Performances} --- The basic algorithm is mostly similar to those + implemented in well known compressors like #bzip# or #bzip2# + (\URL{http://www.muraroa.demon.co.uk}). The adaptive binary coder however + generates small differences. The adaptation noise may cost up to 5\% in + file size, but this penalty is usually offset by the benefits of + adaptation. This is good when processing large and highly structured + files like spreadsheet files. Compression and decompression speed is + about twice slower than #bzip2# but the sorting algorithms is more + robust. Unlike #bzip2# (as of August 1998), this code can compress half a + megabyte of "abababab...." in bounded time. + + Here are some comparative results (in bits per character) obtained on the + Canterbury Corpus (\URL{http://corpus.canterbury.ac.nz}) as of August + 1998. The BSByteStream performance on the single spreadsheet file #Excl# + moves #bzz#'s weighted average ahead of much more sophisticated methods, + like Suzanne Bunton's #fsmxBest# system + \URL{http://corpus.canterbury.ac.nz/methodinfo/fsmx.html}. This result + will not last very long. + + {\footnotesize + \begin{tabular}{lccccccccccccc} + & text & fax & Csrc & Excl & SPRC & tech + & poem & html & lisp & man & play & Weighted & Average \\ + compress + & 3.27 & 0.97 & 3.56 & 2.41 & 4.21 & 3.06 + & 3.38 & 3.68 & 3.90 & 4.43 & 3.51 + & 2.55 & 3.31 \\ + gzip -9 + & 2.85 & 0.82 & 2.24 & 1.63 & 2.67 & 2.71 + & 3.23 & 2.59 & 2.65 & 3.31 & 3.12 + & 2.08 & 2.53 \\ + bzip2 -9 + & 2.27 & 0.78 & 2.18 & 1.01 & 2.70 & 2.02 + & 2.42 & 2.48 & 2.79 & 3.33 & 2.53 + & 1.54 & 2.23 \\ + ppmd + & 2.31 & 0.99 & 2.11 & 1.08 & 2.68 & 2.19 + & 2.48 & 2.38 & 2.43 & 3.00 & 2.53 + & 1.65 & 2.20 \\ + fsmx + & {\bf 2.10} & 0.79 & {\bf 1.89} & 1.48 & {\bf 2.52} & {\bf 1.84} + & {\bf 2.21} & {\bf 2.24} & {\bf 2.29} & {\bf 2.91} & {\bf 2.35} + & 1.63 & {\bf 2.06} \\ + {\bf bzz} + & 2.25 & {\bf 0.76} & 2.13 & {\bf 0.78} & 2.67 & 2.00 + & 2.40 & 2.52 & 2.60 & 3.19 & 2.52 + & {\bf 1.44} & 2.16 + \end{tabular} + } + + Note that the DjVu people have several entries in this table. Program + #compress# was written some time ago by Joe Orost + (\URL{http://www.research.att.com/info/orost}). The #ppmc# method, (a + precursor of #ppmd#) was created by Paul Howard + (\URL{http://www.research.att.com/info/pgh}). The #bzz# program is just + below your eyes. + + @author + L\'eon Bottou <[email protected]> -- Initial implementation\\ + Andrei Erofeev <[email protected]> -- Improved Block Sorting algorithm. + @memo + Simple Burrows-Wheeler general purpose compressor. + @version + #$Id: BSByteStream.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "ByteStream.h" +#include "GException.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** Performs bzz compression/decompression. + + Class #BSByteStream# defines a \Ref{ByteStream} which transparently + performs the BZZ compression/decompression. The constructor of class + \Ref{BSByteStream} takes another \Ref{ByteStream} as argument. Any data + written to the BSByteStream is compressed and written to this second + ByteStream. Any data read from the BSByteStream is internally generated by + decompressing data read from the second ByteStream. + + Program \Ref{bzz} demonstrates how to use this class. All the hard work + is achieved by a simple ByteStream to ByteStream copy, as shown below. + \begin{verbatim} + GP<ByteStream> in=ByteStream::create(infile,"rb"); + GP<ByteStream> out=ByteStream::create(outfile,"wb"); + if (encoding) { + BSByteStream bsb(out, blocksize); + bsb.copy(*in); + } else { + BSByteStream bsb(in); + out->copy(bsb); + } + \end{verbatim} + Due to the block oriented nature of the Burrows-Wheeler transform, there + is a very significant latency between the data input and the data output. + You can use function #flush# to force data output at the expense of + compression efficiency. + + You should never directly access a ByteStream object connected to a valid + BSByteStream object. The ByteStream object can be accessed again after the + destruction of the BSByteStream object. Note that the encoder always + flushes its internal buffers and writes a few final code bytes when the + BSByteStream object is destroyed. Note also that the decoder often reads + a few bytes beyond the last code byte written by the encoder. This lag + means that you must reposition the ByteStream after the destruction of the + BSByteStream object and before re-using the ByteStream object (see + \Ref{IFFByteStream}.) +*/ +class BSByteStream : public ByteStream +{ +public: +// Limits on block sizes + enum { MINBLOCK=10, MAXBLOCK=4096 }; + +// Sorting tresholds + enum { FREQMAX=4, CTXIDS=3 }; + + class Decode; + class Encode; +protected: + BSByteStream(GP<ByteStream> bs); + +public: + /** Creates a BSByteStream. + The BSByteStream will be used for decompressing data. + \begin{description} + \item[Decompression] + The BSByteStream is created and the decompressor initializes. Chunks of + data will be read from ByteStream #bs# and decompressed into an internal + buffer. Function #read# can be used to access the decompressed data. + \end{description} */ + static GP<ByteStream> create(GP<ByteStream> bs); + + /** Constructs a BSByteStream. + The BSByteStream will be used for compressing data. + \begin{description} + \item[Compression] + Set #blocksize# to a positive number smaller than 4096 to + initialize the compressor. Data written to the BSByteStream will be + accumulated into an internal buffer. The buffered data will be + compressed and written to ByteStream #bs# whenever the buffer sizes + reaches the maximum value specified by argument #blocksize# (in + kilobytes). Using a larger block size usually increases the compression + ratio at the expense of computation time. There is no need however to + specify a block size larger than the total number of bytes to compress. + Setting #blocksize# to #1024# is a good starting point. A minimal block + size of 10 is silently enforced. + \end{description} */ + static GP<ByteStream> create(GP<ByteStream> bs, const int blocksize); + + // ByteStream Interface + ~BSByteStream(); + virtual long tell(void) const; + virtual void flush(void) = 0; +protected: + // Data + long offset; + int bptr; + unsigned int blocksize; + int size; + ByteStream *bs; + GP<ByteStream> gbs; + unsigned char *data; + GPBuffer<unsigned char> gdata; + // Coder + GP<ZPCodec> gzp; + BitContext ctx[300]; +private: + // Cancel C++ default stuff + BSByteStream(const BSByteStream &); + BSByteStream & operator=(const BSByteStream &); + BSByteStream(ByteStream *); + BSByteStream(ByteStream *, int); +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp new file mode 100644 index 00000000..9d5b726d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp @@ -0,0 +1,1010 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: BSEncodeByteStream.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 07/1998 + + + +#include "BSByteStream.h" +#include "GString.h" +#undef BSORT_TIMER +#ifdef BSORT_TIMER +#include "GOS.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ======================================== +// --- Assertion + +#define ASSERT(expr) do{if(!(expr))G_THROW("assertion ("#expr") failed");}while(0) + + + +// ======================================== +// --- Global Definitions + + +#ifdef OVERFLOW +#undef OVERFLOW +#endif +// Overflow required when encoding +static const int OVERFLOW=32; + +// Sorting tresholds +static const int RANKSORT_THRESH=10; +static const int QUICKSORT_STACK=512; +static const int PRESORT_THRESH=10; +static const int PRESORT_DEPTH=8; +static const int RADIX_THRESH=32768; + +static const int FREQS0=100000; +static const int FREQS1=1000000; + +// ======================================== +// -- Sorting Routines + + +class _BSort // DJVU_CLASS +{ +public: + ~_BSort(); + _BSort(unsigned char *data, int size); + void run(int &markerpos); +private: + // Members + int size; + unsigned char *data; + unsigned int *posn; + GPBuffer<unsigned int> gposn; + int *rank; + GPBuffer<int> grank; + // Helpers + inline int GT(int p1, int p2, int depth); + inline int GTD(int p1, int p2, int depth); + // -- final in-depth sort + void ranksort(int lo, int hi, int d); + // -- doubling sort + int pivot3r(int *rr, int lo, int hi); + void quicksort3r(int lo, int hi, int d); + // -- presort to depth PRESORT_DEPTH + unsigned char pivot3d(unsigned char *dd, int lo, int hi); + void quicksort3d(int lo, int hi, int d); + // -- radixsort + void radixsort16(void); + void radixsort8(void); +}; + + +// blocksort -- the main entry point + +static void +blocksort(unsigned char *data, int size, int &markerpos) +{ + _BSort bsort(data, size); + bsort.run(markerpos); +} + + +// _BSort construction + +_BSort::_BSort(unsigned char *xdata, int xsize) + : size(xsize), data(xdata), gposn(posn,xsize), grank(rank,xsize+1) +{ + ASSERT(size>0 && size<0x1000000); + rank[size] = -1; +} + +_BSort::~_BSort() +{ +} + + + +// GT -- compare suffixes using rank information + +inline int +_BSort::GT(int p1, int p2, int depth) +{ + int r1, r2; + int twod = depth + depth; + while (1) + { + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + r1=rank[p1+depth]; r2=rank[p2+depth]; + p1+=twod; p2+=twod; + if (r1!=r2) + return (r1>r2); + r1=rank[p1]; r2=rank[p2]; + if (r1!=r2) + return (r1>r2); + }; +} + + +// _BSort::ranksort -- +// -- a simple insertion sort based on GT + +void +_BSort::ranksort(int lo, int hi, int depth) +{ + int i,j; + for (i=lo+1; i<=hi; i++) + { + int tmp = posn[i]; + for(j=i-1; j>=lo && GT(posn[j], tmp, depth); j--) + posn[j+1] = posn[j]; + posn[j+1] = tmp; + } + for(i=lo;i<=hi;i++) + rank[posn[i]]=i; +} + +// pivot -- return suitable pivot + +inline int +_BSort::pivot3r(int *rr, int lo, int hi) +{ + int c1, c2, c3; + if (hi-lo > 256) + { + c1 = pivot3r(rr, lo, (6*lo+2*hi)/8); + c2 = pivot3r(rr, (5*lo+3*hi)/8, (3*lo+5*hi)/8); + c3 = pivot3r(rr, (2*lo+6*hi)/8, hi); + } + else + { + c1 = rr[posn[lo]]; + c2 = rr[posn[(lo+hi)/2]]; + c3 = rr[posn[hi]]; + } + // Extract median + if (c1>c3) + { int tmp=c1; c1=c3; c3=tmp; } + if (c2<=c1) + return c1; + else if (c2>=c3) + return c3; + else + return c2; +} + + +// _BSort::quicksort3r -- Three way quicksort algorithm +// Sort suffixes based on rank at pos+depth +// The algorithm breaks into ranksort when size is +// smaller than RANKSORT_THRESH + +static inline int +mini(int a, int b) +{ + return (a<=b) ? a : b; +} + +static inline void +vswap(int i, int j, int n, unsigned int *x) +{ + while (n-- > 0) + { int tmp = x[i]; x[i++]=x[j]; x[j++]=tmp; } +} + +void +_BSort::quicksort3r(int lo, int hi, int depth) +{ + /* Initialize stack */ + int slo[QUICKSORT_STACK]; + int shi[QUICKSORT_STACK]; + int sp = 1; + slo[0] = lo; + shi[0] = hi; + // Recursion elimination loop + while (--sp>=0) + { + lo = slo[sp]; + hi = shi[sp]; + // Test for insertion sort + if (hi-lo<RANKSORT_THRESH) + { + ranksort(lo, hi, depth); + } + else + { + int tmp; + int *rr=rank+depth; + int med = pivot3r(rr,lo,hi); + // -- positions are organized as follows: + // [lo..l1[ [l1..l[ ]h..h1] ]h1..hi] + // = < > = + int l1 = lo; + int h1 = hi; + while (rr[posn[l1]]==med && l1<h1) { l1++; } + while (rr[posn[h1]]==med && l1<h1) { h1--; } + int l = l1; + int h = h1; + // -- partition set + for (;;) + { + while (l<=h) + { + int c = rr[posn[l]] - med; + if (c > 0) break; + if (c == 0) { tmp=posn[l]; posn[l]=posn[l1]; posn[l1++]=tmp; } + l++; + } + while (l<=h) + { + int c = rr[posn[h]] - med; + if (c < 0) break; + if (c == 0) { tmp=posn[h]; posn[h]=posn[h1]; posn[h1--]=tmp; } + h--; + } + if (l>h) break; + tmp=posn[l]; posn[l]=posn[h]; posn[h]=tmp; + } + // -- reorganize as follows + // [lo..l1[ [l1..h1] ]h1..hi] + // < = > + tmp = mini(l1-lo, l-l1); + vswap(lo, l-tmp, tmp, posn); + l1 = lo + (l-l1); + tmp = mini(hi-h1, h1-h); + vswap(hi-tmp+1, h+1, tmp, posn); + h1 = hi - (h1-h); + // -- process segments + ASSERT(sp+2<QUICKSORT_STACK); + // ----- middle segment (=?) [l1, h1] + for(int i=l1;i<=h1;i++) + rank[posn[i]] = h1; + // ----- lower segment (<) [lo, l1[ + if (l1 > lo) + { + for(int i=lo;i<l1;i++) + rank[posn[i]]=l1-1; + slo[sp]=lo; + shi[sp]=l1-1; + if (slo[sp] < shi[sp]) + sp++; + } + // ----- upper segment (>) ]h1, hi] + if (h1 < hi) + { + slo[sp]=h1+1; + shi[sp]=hi; + if (slo[sp] < shi[sp]) + sp++; + } + } + } +} + + + + + + +// GTD -- compare suffixes using data information +// (up to depth PRESORT_DEPTH) + +inline int +_BSort::GTD(int p1, int p2, int depth) +{ + unsigned char c1, c2; + p1+=depth; p2+=depth; + while (depth < PRESORT_DEPTH) + { + // Perform two + c1=data[p1]; c2=data[p2]; + if (c1!=c2) + return (c1>c2); + c1=data[p1+1]; c2=data[p2+1]; + p1+=2; p2+=2; depth+=2; + if (c1!=c2) + return (c1>c2); + } + if (p1<size && p2<size) + return 0; + return (p1<p2); +} + +// pivot3d -- return suitable pivot + +inline unsigned char +_BSort::pivot3d(unsigned char *rr, int lo, int hi) +{ + unsigned char c1, c2, c3; + if (hi-lo > 256) + { + c1 = pivot3d(rr, lo, (6*lo+2*hi)/8); + c2 = pivot3d(rr, (5*lo+3*hi)/8, (3*lo+5*hi)/8); + c3 = pivot3d(rr, (2*lo+6*hi)/8, hi); + } + else + { + c1 = rr[posn[lo]]; + c2 = rr[posn[(lo+hi)/2]]; + c3 = rr[posn[hi]]; + } + // Extract median + if (c1>c3) + { int tmp=c1; c1=c3; c3=tmp; } + if (c2<=c1) + return c1; + else if (c2>=c3) + return c3; + else + return c2; +} + + +// _BSort::quicksort3d -- Three way quicksort algorithm +// Sort suffixes based on strings until reaching +// depth rank at pos+depth +// The algorithm breaks into ranksort when size is +// smaller than PRESORT_THRESH + +void +_BSort::quicksort3d(int lo, int hi, int depth) +{ + /* Initialize stack */ + int slo[QUICKSORT_STACK]; + int shi[QUICKSORT_STACK]; + int sd[QUICKSORT_STACK]; + int sp = 1; + slo[0] = lo; + shi[0] = hi; + sd[0] = depth; + // Recursion elimination loop + while (--sp>=0) + { + lo = slo[sp]; + hi = shi[sp]; + depth = sd[sp]; + // Test for insertion sort + if (depth >= PRESORT_DEPTH) + { + for (int i=lo; i<=hi; i++) + rank[posn[i]] = hi; + } + else if (hi-lo<PRESORT_THRESH) + { + int i,j; + for (i=lo+1; i<=hi; i++) + { + int tmp = posn[i]; + for(j=i-1; j>=lo && GTD(posn[j], tmp, depth); j--) + posn[j+1] = posn[j]; + posn[j+1] = tmp; + } + for(i=hi;i>=lo;i=j) + { + int tmp = posn[i]; + rank[tmp] = i; + for (j=i-1; j>=lo && !GTD(tmp,posn[j],depth); j--) + rank[posn[j]] = i; + } + } + else + { + int tmp; + unsigned char *dd=data+depth; + unsigned char med = pivot3d(dd,lo,hi); + // -- positions are organized as follows: + // [lo..l1[ [l1..l[ ]h..h1] ]h1..hi] + // = < > = + int l1 = lo; + int h1 = hi; + while (dd[posn[l1]]==med && l1<h1) { l1++; } + while (dd[posn[h1]]==med && l1<h1) { h1--; } + int l = l1; + int h = h1; + // -- partition set + for (;;) + { + while (l<=h) + { + int c = (int)dd[posn[l]] - (int)med; + if (c > 0) break; + if (c == 0) { tmp=posn[l]; posn[l]=posn[l1]; posn[l1++]=tmp; } + l++; + } + while (l<=h) + { + int c = (int)dd[posn[h]] - (int)med; + if (c < 0) break; + if (c == 0) { tmp=posn[h]; posn[h]=posn[h1]; posn[h1--]=tmp; } + h--; + } + if (l>h) break; + tmp=posn[l]; posn[l]=posn[h]; posn[h]=tmp; + } + // -- reorganize as follows + // [lo..l1[ [l1..h1] ]h1..hi] + // < = > + tmp = mini(l1-lo, l-l1); + vswap(lo, l-tmp, tmp, posn); + l1 = lo + (l-l1); + tmp = mini(hi-h1, h1-h); + vswap(hi-tmp+1, h+1, tmp, posn); + h1 = hi - (h1-h); + // -- process segments + ASSERT(sp+3<QUICKSORT_STACK); + // ----- middle segment (=?) [l1, h1] + l = l1; h = h1; + if (med==0) // special case for marker [slow] + for (int i=l; i<=h; i++) + if ((int)posn[i]+depth == size-1) + { + tmp=posn[i]; posn[i]=posn[l]; posn[l]=tmp; + rank[tmp]=l++; break; + } + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth+1; } + else if (l==h) + { rank[posn[h]] = h; } + // ----- lower segment (<) [lo, l1[ + l = lo; + h = l1-1; + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth; } + else if (l==h) + { rank[posn[h]] = h; } + // ----- upper segment (>) ]h1, hi] + l = h1+1; + h = hi; + if (l<h) + { slo[sp] = l; shi[sp] = h; sd[sp++] = depth; } + else if (l==h) + { rank[posn[h]] = h; } + } + } +} + + + + +// _BSort::radixsort8 -- 8 bit radix sort + +void +_BSort::radixsort8(void) +{ + int i; + // Initialize frequency array + int lo[256], hi[256]; + for (i=0; i<256; i++) + hi[i] = lo[i] = 0; + // Count occurences + for (i=0; i<size-1; i++) + hi[data[i]] ++; + // Compute positions (lo) + int last = 1; + for (i=0; i<256; i++) + { + lo[i] = last; + hi[i] = last + hi[i] - 1; + last = hi[i] + 1; + } + for (i=0; i<size-1; i++) + { + posn[ lo[data[i]]++ ] = i; + rank[ i ] = hi[data[i]]; + } + // Process marker "$" + posn[0] = size-1; + rank[size-1] = 0; + // Extra element + rank[size] = -1; +} + + +// _BSort::radixsort16 -- 16 bit radix sort + +void +_BSort::radixsort16(void) +{ + int i; + // Initialize frequency array + int *ftab; + GPBuffer<int> gftab(ftab,65536); + for (i=0; i<65536; i++) + ftab[i] = 0; + // Count occurences + unsigned char c1 = data[0]; + for (i=0; i<size-1; i++) + { + unsigned char c2 = data[i+1]; + ftab[(c1<<8)|c2] ++; + c1 = c2; + } + // Generate upper position + for (i=1;i<65536;i++) + ftab[i] += ftab[i-1]; + // Fill rank array with upper bound + c1 = data[0]; + for (i=0; i<size-2; i++) + { + unsigned char c2 = data[i+1]; + rank[i] = ftab[(c1<<8)|c2]; + c1 = c2; + } + // Fill posn array (backwards) + c1 = data[size-2]; + for (i=size-3; i>=0; i--) + { + unsigned char c2 = data[i]; + posn[ ftab[(c2<<8)|c1]-- ] = i; + c1 = c2; + } + // Fixup marker stuff + ASSERT(data[size-1]==0); + c1 = data[size-2]; + posn[0] = size-1; + posn[ ftab[(c1<<8)] ] = size-2; + rank[size-1] = 0; + rank[size-2] = ftab[(c1<<8)]; + // Extra element + rank[size] = -1; +} + + + +// _BSort::run -- main sort loop + +void +_BSort::run(int &markerpos) +{ + int lo, hi; + ASSERT(size>0); + ASSERT(data[size-1]==0); +#ifdef BSORT_TIMER + long start = GOS::ticks(); +#endif + // Step 1: Radix sort + int depth = 0; + if (size > RADIX_THRESH) + { + radixsort16(); + depth=2; + } + else + { + radixsort8(); + depth=1; + } + // Step 2: Perform presort to depth PRESORT_DEPTH + for (lo=0; lo<size; lo++) + { + hi = rank[posn[lo]]; + if (lo < hi) + quicksort3d(lo, hi, depth); + lo = hi; + } + depth = PRESORT_DEPTH; +#ifdef BSORT_TIMER + long middle = GOS::ticks(); +#endif + // Step 3: Perform rank doubling + int again = 1; + while (again) + { + again = 0; + int sorted_lo = 0; + for (lo=0; lo<size; lo++) + { + hi = rank[posn[lo]&0xffffff]; + if (lo == hi) + { + lo += (posn[lo]>>24) & 0xff; + } + else + { + if (hi-lo < RANKSORT_THRESH) + { + ranksort(lo, hi, depth); + } + else + { + again += 1; + while (sorted_lo < lo-1) + { + int step = mini(255, lo-1-sorted_lo); + posn[sorted_lo] = (posn[sorted_lo]&0xffffff) | (step<<24); + sorted_lo += step+1; + } + quicksort3r(lo, hi, depth); + sorted_lo = hi + 1; + } + lo = hi; + } + } + // Finish threading + while (sorted_lo < lo-1) + { + int step = mini(255, lo-1-sorted_lo); + posn[sorted_lo] = (posn[sorted_lo]&0xffffff) | (step<<24); + sorted_lo += step+1; + } + // Double depth + depth += depth; + } + // Step 4: Permute data + int i; + markerpos = -1; + for (i=0; i<size; i++) + rank[i] = data[i]; + for (i=0; i<size; i++) + { + int j = posn[i] & 0xffffff; + if (j>0) + { + data[i] = rank[j-1]; + } + else + { + data[i] = 0; + markerpos = i; + } + } + ASSERT(markerpos>=0 && markerpos<size); +#ifdef BSORT_TIMER + long end = GOS::ticks(); + DjVuPrintErrorUTF8("Sorting time: %d bytes in %ld + %ld = %ld ms\n", + size-1, middle-start, end-middle, end-start); +#endif +} + + +// ======================================== +// -- Encoding + +static void +encode_raw(ZPCodec &zp, int bits, int x) +{ + int n = 1; + int m = (1<<bits); + while (n < m) + { + x = (x & (m-1)) << 1; + int b = (x >> bits); + zp.encoder(b); + n = (n<<1) | b; + } +} + +static inline void +encode_binary(ZPCodec &zp, BitContext *ctx, int bits, int x) +{ + // Require 2^bits-1 contexts + int n = 1; + int m = (1<<bits); + ctx = ctx - 1; + while (n < m) + { + x = (x & (m-1)) << 1; + int b = (x >> bits); + zp.encoder(b, ctx[n]); + n = (n<<1) | b; + } +} + +class BSByteStream::Encode : public BSByteStream +{ +public: + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Encode(GP<ByteStream> bs); + ~Encode(); + void init(const int encoding); + // Virtual functions + virtual size_t write(const void *buffer, size_t sz); + virtual void flush(void); +protected: + unsigned int encode(void); +}; + +unsigned int +BSByteStream::Encode::encode() +{ + ///////////////////////////////// + //////////// Block Sort Tranform + + int markerpos = size-1; + blocksort(data,size,markerpos); + + ///////////////////////////////// + //////////// Encode Output Stream + + // Header + ZPCodec &zp=*gzp; + encode_raw(zp, 24, size); + // Determine and Encode Estimation Speed + int fshift = 0; + if (size < FREQS0) + { fshift=0; zp.encoder(0); } + else if (size < FREQS1) + { fshift = 1; zp.encoder(1); zp.encoder(0); } + else + { fshift = 2; zp.encoder(1); zp.encoder(1); } + // MTF + unsigned char mtf[256]; + unsigned char rmtf[256]; + unsigned int freq[FREQMAX]; + int m = 0; + for (m=0; m<256; m++) + mtf[m] = m; + for (m=0; m<256; m++) + rmtf[mtf[m]] = m; + int fadd = 4; + for (m=0; m<FREQMAX; m++) + freq[m] = 0; + // Encode + int i; + int mtfno = 3; + for (i=0; i<size; i++) + { + // Get MTF data + int c = data[i]; + int ctxid = CTXIDS-1; + if (ctxid>mtfno) ctxid=mtfno; + mtfno = rmtf[c]; + if (i==markerpos) + mtfno = 256; + // Encode using ZPCoder + int b; + BitContext *cx = ctx; + b = (mtfno==0); + zp.encoder(b, cx[ctxid]); + if (b) goto rotate; cx+=CTXIDS; + b = (mtfno==1); + zp.encoder(b, cx[ctxid]); + if (b) goto rotate; cx+=CTXIDS; + b = (mtfno<4); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,1,mtfno-2); goto rotate; } + cx+=1+1; + b = (mtfno<8); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,2,mtfno-4); goto rotate; } + cx+=1+3; + b = (mtfno<16); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,3,mtfno-8); goto rotate; } + cx+=1+7; + b = (mtfno<32); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,4,mtfno-16); goto rotate; } + cx+=1+15; + b = (mtfno<64); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,5,mtfno-32); goto rotate; } + cx+=1+31; + b = (mtfno<128); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,6,mtfno-64); goto rotate; } + cx+=1+63; + b = (mtfno<256); + zp.encoder(b, cx[0]); + if (b) { encode_binary(zp,cx+1,7,mtfno-128); goto rotate; } + continue; + // Rotate MTF according to empirical frequencies (new!) + rotate: + // Adjust frequencies for overflow + fadd = fadd + (fadd>>fshift); + if (fadd > 0x10000000) + { + fadd = fadd>>24; + freq[0] >>= 24; + freq[1] >>= 24; + freq[2] >>= 24; + freq[3] >>= 24; + for (int k=4; k<FREQMAX; k++) + freq[k] = freq[k]>>24; + } + // Relocate new char according to new freq + unsigned int fc = fadd; + if (mtfno < FREQMAX) + fc += freq[mtfno]; + int k; + for (k=mtfno; k>=FREQMAX; k--) + { + mtf[k] = mtf[k-1]; + rmtf[mtf[k]] = k; + } + for (; k>0 && fc>=freq[k-1]; k--) + { + mtf[k] = mtf[k-1]; + freq[k] = freq[k-1]; + rmtf[mtf[k]] = k; + } + mtf[k] = c; + freq[k] = fc; + rmtf[mtf[k]] = k; + } + // Terminate + return 0; +} + +// ======================================== +// --- Construction + +BSByteStream::Encode::Encode(GP<ByteStream> xbs) +: BSByteStream(xbs) {} + +void +BSByteStream::Encode::init(const int xencoding) +{ + gzp=ZPCodec::create(gbs,true,true); + const int encoding=(xencoding<MINBLOCK)?MINBLOCK:xencoding; + if (encoding > MAXBLOCK) + G_THROW( ERR_MSG("ByteStream.blocksize") "\t" + GUTF8String(MAXBLOCK) ); + // Record block size + blocksize = encoding * 1024; + // Initialize context array +} + +BSByteStream::Encode::~Encode() +{ + // Flush + flush(); + // Encode EOF marker + encode_raw(*gzp, 24, 0); + // Free allocated memory +} + +GP<ByteStream> +BSByteStream::create(GP<ByteStream> xbs,const int blocksize) +{ + BSByteStream::Encode *rbs=new BSByteStream::Encode(xbs); + GP<ByteStream> retval=rbs; + rbs->init(blocksize); + return retval; +} + +// ======================================== +// -- ByteStream interface + +void +BSByteStream::Encode::flush() +{ + if (bptr>0) + { + ASSERT(bptr<(int)blocksize); + memset(data+bptr, 0, OVERFLOW); + size = bptr+1; + encode(); + } + size = bptr = 0; +} + +size_t +BSByteStream::Encode::write(const void *buffer, size_t sz) +{ + // Trivial checks + if (sz == 0) + return 0; + // Loop + int copied = 0; + while (sz > 0) + { + // Initialize + if (!data) + { + bptr = 0; + gdata.resize(blocksize+OVERFLOW); + } + // Compute remaining + int bytes = blocksize - 1 - bptr; + if (bytes > (int)sz) + bytes = sz; + // Store date (todo: rle) + memcpy(data+bptr, buffer, bytes); + buffer = (void*)((char*)buffer + bytes); + bptr += bytes; + sz -= bytes; + copied += bytes; + offset += bytes; + // Flush when needed + if (bptr + 1 >= (int)blocksize) + flush(); + } + // return + return copied; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp new file mode 100644 index 00000000..011b348d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp @@ -0,0 +1,1445 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ByteStream.cpp,v 1.18 2004/08/06 14:50:05 leonb Exp $ +// $Name: release_3_5_15 $ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 04/1997 + +#include "DjVuGlobal.h" +#include "ByteStream.h" +#include "GOS.h" +#include "GURL.h" +#include "DjVuMessage.h" +#include <fcntl.h> +#if defined(WIN32) || defined(__CYGWIN32__) +# include <io.h> +#endif + +#ifdef UNIX +# ifndef HAS_MEMMAP +# define HAS_MEMMAP 1 +# endif +#endif + +#if defined(UNIX) +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +# include <errno.h> +# ifdef HAS_MEMMAP +# include <sys/mman.h> +# endif +#elif defined(macintosh) +# include <unistd.h> +_MSL_IMP_EXP_C int _dup(int); +_MSL_IMP_EXP_C int _dup2(int,int); +_MSL_IMP_EXP_C int _close(int); +__inline int dup(int _a ) { return _dup(_a);} +__inline int dup2(int _a, int _b ) { return _dup2(_a, _b);} +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +const char *ByteStream::EndOfFile=ERR_MSG("EOF"); + +/** ByteStream interface for stdio files. + The virtual member functions #read#, #write#, #tell# and #seek# are mapped + to the well known stdio functions #fread#, #fwrite#, #ftell# and #fseek#. + @see Unix man page fopen(3), fread(3), fwrite(3), ftell(3), fseek(3) */ + +class ByteStream::Stdio : public ByteStream { +public: + Stdio(void); + + /** Constructs a ByteStream for accessing the file named #url#. + Arguments #url# and #mode# are similar to the arguments of the well + known stdio function #fopen#. In addition a url of #-# will be + interpreted as the standard output or the standard input according to + #mode#. This constructor will open a stdio file and construct a + ByteStream object accessing this file. Destroying the ByteStream object + will flush and close the associated stdio file. Returns an error code + if the stdio file cannot be opened. */ + GUTF8String init(const GURL &url, const char * const mode); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + GUTF8String init(FILE * const f, const char * const mode="rb", const bool closeme=false); + + /** Initializes from stdio */ + GUTF8String init(const char mode[]); + + // Virtual functions + ~Stdio(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual void flush(void); + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + virtual long tell(void) const; +private: + // Cancel C++ default stuff + Stdio(const Stdio &); + Stdio & operator=(const Stdio &); +private: + // Implementation + bool can_read; + bool can_write; + bool must_close; +protected: + FILE *fp; + long pos; +}; + +inline GUTF8String +ByteStream::Stdio::init(FILE * const f,const char mode[],const bool closeme) +{ + fp=f; + must_close=closeme; + return init(mode); +} + + +/** ByteStream interface managing a memory buffer. + Class #ByteStream::Memory# manages a dynamically resizable buffer from + which data can be read or written. The buffer itself is organized as an + array of blocks of 4096 bytes. */ + +class ByteStream::Memory : public ByteStream +{ +public: + /** Constructs an empty ByteStream::Memory. + The buffer is initially empty. You must first use function #write# + to store data into the buffer, use function #seek# to rewind the + current position, and function #read# to read the data back. */ + Memory(); + /** Constructs a Memory by copying initial data. The + Memory buffer is initialized with #size# bytes copied from the + memory area pointed to by #buffer#. */ + GUTF8String init(const void * const buffer, const size_t size); + // Virtual functions + ~Memory(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual int seek(long offset, int whence=SEEK_SET, bool nothrow=false); + virtual long tell(void) const; + /** Erases everything in the Memory. + The current location is reset to zero. */ + void empty(); + /** Returns the total number of bytes contained in the buffer. Valid + offsets for function #seek# range from 0 to the value returned by this + function. */ + virtual int size(void) const; + /** Returns a reference to the byte at offset #n#. This reference can be + used to read (as in #mbs[n]#) or modify (as in #mbs[n]=c#) the contents + of the buffer. */ + char &operator[] (int n); + /** Copies all internal data into \Ref{TArray} and returns it */ +private: + // Cancel C++ default stuff + Memory(const Memory &); + Memory & operator=(const Memory &); + // Current position + int where; +protected: + /** Reads data from a random position. This function reads at most #sz# + bytes at position #pos# into #buffer# and returns the actual number of + bytes read. The current position is unchanged. */ + virtual size_t readat(void *buffer, size_t sz, int pos); + /** Number of bytes in internal buffer. */ + int bsize; + /** Number of 4096 bytes blocks. */ + int nblocks; + /** Pointers (possibly null) to 4096 bytes blocks. */ + char **blocks; + /** Pointers (possibly null) to 4096 bytes blocks. */ + GPBuffer<char *> gblocks; +}; + + + +inline int +ByteStream::Memory::size(void) const +{ + return bsize; +} + +inline char & +ByteStream::Memory::operator[] (int n) +{ + return blocks[n>>12][n&0xfff]; +} + + + +/** Read-only ByteStream interface to a memory area. + Class #ByteStream::Static# implements a read-only ByteStream interface for a + memory area specified by the user at construction time. Calls to function + #read# directly access this memory area. The user must therefore make + sure that its content remain valid long enough. */ + +class ByteStream::Static : public ByteStream +{ +public: + class Allocate; + class Duplicate; + friend class Duplicate; + + /** Creates a Static object for allocating the memory area of + length #sz# starting at address #buffer#. */ + Static(const void * const buffer, const size_t sz); + ~Static(); + // Virtual functions + virtual size_t read(void *buffer, size_t sz); + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + virtual long tell(void) const; + /** Returns the total number of bytes contained in the buffer, file, etc. + Valid offsets for function #seek# range from 0 to the value returned + by this function. */ + virtual int size(void) const; + virtual GP<ByteStream> duplicate(const size_t xsize) const; + /// Returns false, unless a subclass of ByteStream::Static + virtual bool is_static(void) const { return true; } +protected: + const char *data; + int bsize; +private: + int where; +}; + +ByteStream::Static::~Static() {} + +class ByteStream::Static::Allocate : public ByteStream::Static +{ +public: + friend class ByteStream; +protected: + char *buf; + GPBuffer<char> gbuf; +public: + Allocate(const size_t size) : Static(0,size), gbuf(buf,size) { data=buf; } + virtual ~Allocate(); +}; + +ByteStream::Static::Allocate::~Allocate() {} + +inline int +ByteStream::Static::size(void) const +{ + return bsize; +} + +class ByteStream::Static::Duplicate : public ByteStream::Static +{ +protected: + GP<ByteStream> gbs; +public: + Duplicate(const ByteStream::Static &bs, const size_t size); +}; + +ByteStream::Static::Duplicate::Duplicate( + const ByteStream::Static &bs, const size_t xsize) +: ByteStream::Static(0,0) +{ + if(xsize&&(bs.bsize<bs.where)) + { + const size_t bssize=(size_t)bs.bsize-(size_t)bs.where; + bsize=(size_t)((xsize>bssize)?bssize:xsize); + gbs=const_cast<ByteStream::Static *>(&bs); + data=bs.data+bs.where; + } +} + +GP<ByteStream> +ByteStream::Static::duplicate(const size_t xsize) const +{ + return new ByteStream::Static::Duplicate(*this,xsize); +} + +#if HAS_MEMMAP +/** Read-only ByteStream interface to a memmap area. + Class #MemoryMapByteStream# implements a read-only ByteStream interface + for a memory map to a file. */ + +class MemoryMapByteStream : public ByteStream::Static +{ +public: + MemoryMapByteStream(void); + virtual ~MemoryMapByteStream(); +private: + GUTF8String init(const int fd, const bool closeme); + GUTF8String init(FILE *const f,const bool closeme); + friend class ByteStream; +}; +#endif + +//// CLASS BYTESTREAM + + +ByteStream::~ByteStream() +{ +} + +int +ByteStream::scanf(const char *fmt, ...) +{ + G_THROW( ERR_MSG("ByteStream.not_implemented") ); // This is a place holder function. + return 0; +} + +size_t +ByteStream::read(void *buffer, size_t sz) +{ + G_THROW( ERR_MSG("ByteStream.cant_read") ); // Cannot read from a ByteStream created for writing + return 0; +} + +size_t +ByteStream::write(const void *buffer, size_t sz) +{ + G_THROW( ERR_MSG("ByteStream.cant_write") ); // Cannot write from a ByteStream created for reading + return 0; +} + +void +ByteStream::flush() +{ +} + +int +ByteStream::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + int ncurrent = tell(); + switch (whence) + { + case SEEK_SET: + nwhere=0; break; + case SEEK_CUR: + nwhere=ncurrent; break; + case SEEK_END: + { + if(offset) + { + if (nothrow) + return -1; + G_THROW( ERR_MSG("ByteStream.backward") ); + } + char buffer[1024]; + int bytes; + while((bytes=read(buffer, sizeof(buffer)))) + EMPTY_LOOP; + return 0; + } + default: + G_THROW( ERR_MSG("ByteStream.bad_arg") ); // Illegal argument in seek + } + nwhere += offset; + if (nwhere < ncurrent) + { + // Seeking backwards is not supported by this ByteStream + if (nothrow) + return -1; + G_THROW( ERR_MSG("ByteStream.backward") ); + } + while (nwhere>ncurrent) + { + char buffer[1024]; + const int xbytes=(ncurrent+(int)sizeof(buffer)>nwhere) + ?(nwhere - ncurrent):(int)sizeof(buffer); + const int bytes = read(buffer, xbytes); + ncurrent += bytes; + if (!bytes) + G_THROW( ByteStream::EndOfFile ); + // Seeking works funny on this ByteStream (ftell() acts strange) + if (ncurrent!=tell()) + G_THROW( ERR_MSG("ByteStream.seek") ); + } + return 0; +} + +size_t +ByteStream::readall(void *buffer, size_t size) +{ + size_t total = 0; + while (size > 0) + { + int nitems = read(buffer, size); + // Replaced perror() below with G_THROW(). It still makes little sense + // as there is no guarantee, that errno is right. Still, throwing + // exception instead of continuing to loop is better. + // - eaf + if(nitems < 0) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + if (nitems == 0) + break; + total += nitems; + size -= nitems; + buffer = (void*)((char*)buffer + nitems); + } + return total; +} + +size_t +ByteStream::format(const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + return writestring(message); +} + +size_t +ByteStream::writestring(const GNativeString &s) +{ + int retval; + if(cp != UTF8) + { + retval=writall((const char *)s,s.length()); + if(cp == AUTO) + cp=NATIVE; // Avoid mixing string types. + }else + { + const GUTF8String msg(s.getNative2UTF8()); + retval=writall((const char *)msg,msg.length()); + } + return retval; +} + +size_t +ByteStream::writestring(const GUTF8String &s) +{ + int retval; + if(cp != NATIVE) + { + retval=writall((const char *)s,s.length()); + if(cp == AUTO) + cp=UTF8; // Avoid mixing string types. + }else + { + const GNativeString msg(s.getUTF82Native()); + retval=writall((const char *)msg,msg.length()); + } + return retval; +} + +size_t +ByteStream::writall(const void *buffer, size_t size) +{ + size_t total = 0; + while (size > 0) + { + size_t nitems = write(buffer, size); + if (nitems == 0) + G_THROW( ERR_MSG("ByteStream.write_error") ); // Unknown error in write + total += nitems; + size -= nitems; + buffer = (void*)((char*)buffer + nitems); + } + return total; +} + +size_t +ByteStream::copy(ByteStream &bsfrom, size_t size) +{ + size_t total = 0; + const size_t max_buffer_size=200*1024; + const size_t buffer_size=(size>0 && size<max_buffer_size) + ?size:max_buffer_size; + char *buffer; + GPBuffer<char> gbuf(buffer,buffer_size); + for(;;) + { + size_t bytes = buffer_size; + if (size>0 && bytes+total>size) + bytes = size - total; + if (bytes == 0) + break; + bytes = bsfrom.read((void*)buffer, bytes); + if (bytes == 0) + break; + writall((void*)buffer, bytes); + total += bytes; + } + return total; +} + + +void +ByteStream::write8 (unsigned int card) +{ + unsigned char c[1]; + c[0] = (card) & 0xff; + if (write((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write16(unsigned int card) +{ + unsigned char c[2]; + c[0] = (card>>8) & 0xff; + c[1] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write24(unsigned int card) +{ + unsigned char c[3]; + c[0] = (card>>16) & 0xff; + c[1] = (card>>8) & 0xff; + c[2] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +void +ByteStream::write32(unsigned int card) +{ + unsigned char c[4]; + c[0] = (card>>24) & 0xff; + c[1] = (card>>16) & 0xff; + c[2] = (card>>8) & 0xff; + c[3] = (card) & 0xff; + if (writall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +unsigned int +ByteStream::read8 () +{ + unsigned char c[1]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return c[0]; +} + +unsigned int +ByteStream::read16() +{ + unsigned char c[2]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (c[0]<<8)+c[1]; +} + +unsigned int +ByteStream::read24() +{ + unsigned char c[3]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (((c[0]<<8)+c[1])<<8)+c[2]; +} + +unsigned int +ByteStream::read32() +{ + unsigned char c[4]; + if (readall((void*)c, sizeof(c)) != sizeof(c)) + G_THROW( ByteStream::EndOfFile ); + return (((((c[0]<<8)+c[1])<<8)+c[2])<<8)+c[3]; +} + + + +//// CLASS ByteStream::Stdio + +ByteStream::Stdio::Stdio(void) +: can_read(false),can_write(false),must_close(true),fp(0),pos(0) +{} + +ByteStream::Stdio::~Stdio() +{ + if (fp && must_close) + fclose(fp); +} + +GUTF8String +ByteStream::Stdio::init(const char mode[]) +{ + char const *mesg=0; + bool binary=false; + if(!fp) + must_close=false; + for (const char *s=mode; s && *s; s++) + { + switch(*s) + { + case 'r': + can_read=true; + if(!fp) fp=stdin; + break; + case 'w': + case 'a': + can_write=true; + if(!fp) fp=stdout; + break; + case '+': + can_read=can_write=true; + break; + case 'b': + binary=true; + break; + default: + mesg= ERR_MSG("ByteStream.bad_mode"); // Illegal mode in Stdio + } + } + if(binary && fp) { +#if defined(__CYGWIN32__) + setmode(fileno(fp), O_BINARY); +#elif defined(WIN32) + _setmode(_fileno(fp), _O_BINARY); +#endif + } + GUTF8String retval; + if(!mesg) + { + tell(); + }else + { + retval=mesg; + } + if(mesg &&(fp && must_close)) + { + fclose(fp); + fp=0; + must_close=false; + } + return retval; +} + +static FILE * +urlfopen(const GURL &url,const char mode[]) +{ +#ifdef WIN32 + FILE *retval=0; + const GUTF8String filename(url.UTF8Filename()); + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + if(filename.ncopy(wfilename,wfilename_size) > 0) + { + const GUTF8String gmode(mode); + wchar_t *wmode; + const size_t wmode_size=gmode.length()+1; + GPBuffer<wchar_t> gwmode(wmode,wmode_size); + if(gmode.ncopy(wmode,wmode_size) > 0) + { + retval=_wfopen(wfilename,wmode); + } + } + return retval?retval:fopen((const char *)url.NativeFilename(),mode); +#else + return fopen((const char *)url.NativeFilename(),mode); +#endif +} + +#ifdef UNIX +static int +urlopen(const GURL &url, const int mode, const int perm) +{ + return open((const char *)url.NativeFilename(),mode,perm); +} +#endif /* UNIX */ + +GUTF8String +ByteStream::Stdio::init(const GURL &url, const char mode[]) +{ + GUTF8String retval; + if (url.fname() != "-") + { + fp = urlfopen(url,mode); + if (!fp) + { + // Failed to open '%s': %s + G_THROW( ERR_MSG("ByteStream.open_fail") "\t" + url.name() + +"\t"+GNativeString(strerror(errno)).getNative2UTF8()); + } + } + return retval.length()?retval:init(mode); +} + +size_t +ByteStream::Stdio::read(void *buffer, size_t size) +{ + if (!can_read) + G_THROW( ERR_MSG("ByteStream.no_read") ); // Stdio not opened for reading + size_t nitems; + do + { + clearerr(fp); + nitems = fread(buffer, 1, size, fp); + if (nitems<=0 && ferror(fp)) + { +#ifdef EINTR + if (errno!=EINTR) +#endif + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + else + break; + } while(true); + pos += nitems; + return nitems; +} + +size_t +ByteStream::Stdio::write(const void *buffer, size_t size) +{ + if (!can_write) + G_THROW( ERR_MSG("ByteStream.no_write") ); // Stdio not opened for writing + size_t nitems; + do + { + clearerr(fp); + nitems = fwrite(buffer, 1, size, fp); + if (nitems<=0 && ferror(fp)) + { +#ifdef EINTR + if (errno!=EINTR) +#endif + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + else + break; + } while(true); + pos += nitems; + return nitems; +} + +void +ByteStream::Stdio::flush() +{ + if (fflush(fp) < 0) + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) +} + +long +ByteStream::Stdio::tell(void) const +{ + long x = ftell(fp); + if (x >= 0) + { + Stdio *sbs=const_cast<Stdio *>(this); + (sbs->pos) = x; + }else + { + x=pos; + } + return x; +} + +int +ByteStream::Stdio::seek(long offset, int whence, bool nothrow) +{ + if (whence==SEEK_SET && offset>=0 && offset==ftell(fp)) + return 0; + clearerr(fp); + if (fseek(fp, offset, whence)) + { + if (nothrow) + return -1; + G_THROW(strerror(errno)); // (No error in the DjVuMessageFile) + } + return tell(); +} + + + + +///////// ByteStream::Memory + +ByteStream::Memory::Memory() + : where(0), bsize(0), nblocks(0), gblocks(blocks,0) +{ +} + +GUTF8String +ByteStream::Memory::init(void const * const buffer, const size_t sz) +{ + GUTF8String retval; + G_TRY + { + writall(buffer, sz); + where = 0; + } + G_CATCH(ex) // The only error that should be thrown is out of memory... + { + retval=ex.get_cause(); + } + G_ENDCATCH; + return retval; +} + +void +ByteStream::Memory::empty() +{ + for (int b=0; b<nblocks; b++) + { + delete [] blocks[b]; + blocks[b]=0; + } + bsize = 0; + where = 0; + nblocks = 0; +} + +ByteStream::Memory::~Memory() +{ + empty(); +} + +size_t +ByteStream::Memory::write(const void *buffer, size_t sz) +{ + int nsz = (int)sz; + if (nsz <= 0) + return 0; + // check memory + if ( (where+nsz) > ((bsize+0xfff)&~0xfff) ) + { + // reallocate pointer array + if ( (where+nsz) > (nblocks<<12) ) + { + const int old_nblocks=nblocks; + nblocks = (((where+nsz)+0xffff)&~0xffff) >> 12; + gblocks.resize(nblocks); + char const ** eblocks=(char const **)(blocks+old_nblocks); + for(char const * const * const new_eblocks=blocks+nblocks; + eblocks <new_eblocks; eblocks++) + { + *eblocks = 0; + } + } + // allocate blocks + for (int b=(where>>12); (b<<12)<(where+nsz); b++) + { + if (! blocks[b]) + blocks[b] = new char[0x1000]; + } + } + // write data to buffer + while (nsz > 0) + { + int n = (where|0xfff) + 1 - where; + n = ((nsz < n) ? nsz : n); + memcpy( (void*)&blocks[where>>12][where&0xfff], buffer, n); + buffer = (void*) ((char*)buffer + n); + where += n; + nsz -= n; + } + // adjust size + if (where > bsize) + bsize = where; + return sz; +} + +size_t +ByteStream::Memory::readat(void *buffer, size_t sz, int pos) +{ + if ((int) sz > bsize - pos) + sz = bsize - pos; + int nsz = (int)sz; + if (nsz <= 0) + return 0; + // read data from buffer + while (nsz > 0) + { + int n = (pos|0xfff) + 1 - pos; + n = ((nsz < n) ? nsz : n); + memcpy(buffer, (void*)&blocks[pos>>12][pos&0xfff], n); + buffer = (void*) ((char*)buffer + n); + pos += n; + nsz -= n; + } + return sz; +} + +size_t +ByteStream::Memory::read(void *buffer, size_t sz) +{ + sz = readat(buffer,sz,where); + where += sz; + return sz; +} + +long +ByteStream::Memory::tell(void) const +{ + return where; +} + +int +ByteStream::Memory::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + switch (whence) + { + case SEEK_SET: nwhere = 0; break; + case SEEK_CUR: nwhere = where; break; + case SEEK_END: nwhere = bsize; break; + default: G_THROW( ERR_MSG("bad_arg") "\tByteStream::Memory::seek()"); // Illegal argument in ByteStream::Memory::seek() + } + nwhere += offset; + if (nwhere<0) + G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file + where = nwhere; + return 0; +} + + + +/** This function has been moved into Arrays.cpp + In order to avoid dependencies from ByteStream.o + to Arrays.o */ +#ifdef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP +TArray<char> +ByteStream::get_data(void) +{ + TArray<char> data(0, size()-1); + readat((char*)data, size(), 0); + return data; +} +#endif + + +///////// ByteStream::Static + +ByteStream::Static::Static(const void * const buffer, const size_t sz) + : data((const char *)buffer), bsize(sz), where(0) +{ +} + +size_t +ByteStream::Static::read(void *buffer, size_t sz) +{ + int nsz = (int)sz; + if (nsz > bsize - where) + nsz = bsize - where; + if (nsz <= 0) + return 0; + memcpy(buffer, data+where, nsz); + where += nsz; + return nsz; +} + +int +ByteStream::Static::seek(long offset, int whence, bool nothrow) +{ + int nwhere = 0; + switch (whence) + { + case SEEK_SET: nwhere = 0; break; + case SEEK_CUR: nwhere = where; break; + case SEEK_END: nwhere = bsize; break; + default: G_THROW("bad_arg\tByteStream::Static::seek()"); // Illegal argument to ByteStream::Static::seek() + } + nwhere += offset; + if (nwhere<0) + G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file + where = nwhere; + return 0; +} + +long +ByteStream::Static::tell(void) const +{ + return where; +} + +GP<ByteStream> +ByteStream::create(void) +{ + return new Memory(); +} + +GP<ByteStream> +ByteStream::create(void const * const buffer, const size_t size) +{ + Memory *mbs=new Memory(); + GP<ByteStream> retval=mbs; + mbs->init(buffer,size); + return retval; +} + +GP<ByteStream> +ByteStream::create(const GURL &url,char const * const xmode) +{ + GP<ByteStream> retval; + const char *mode = ((xmode) ? xmode : "rb"); +#ifdef UNIX + if (!strcmp(mode,"rb")) + { + int fd = urlopen(url,O_RDONLY,0777); + if (fd >= 0) + { +#if HAS_MEMMAP && defined(S_IFREG) + struct stat buf; + if ( (fstat(fd, &buf) >= 0) && (buf.st_mode & S_IFREG) ) + { + MemoryMapByteStream *rb = new MemoryMapByteStream(); + retval = rb; + GUTF8String errmessage = rb->init(fd,true); + if(errmessage.length()) + retval=0; + } +#endif + if (! retval) + { + FILE *f = fdopen(fd, mode); + if (f) + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f, mode, true); + if(errmessage.length()) + retval=0; + } + } + if (! retval) + close(fd); + } + } +#endif + if (! retval) + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(url, mode); + if(errmessage.length()) + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(char const * const mode) +{ + GP<ByteStream> retval; + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(mode?mode:"rb"); + if(errmessage.length()) + { + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(const int fd,char const * const mode,const bool closeme) +{ + GP<ByteStream> retval; + const char *default_mode="rb"; +#if HAS_MEMMAP + if ( (!mode&&(fd!=0)&&(fd!=1)&&(fd!=2)) + || (mode&&(GUTF8String("rb") == mode))) + { + MemoryMapByteStream *rb=new MemoryMapByteStream(); + retval=rb; + GUTF8String errmessage=rb->init(fd,closeme); + if(errmessage.length()) + { + retval=0; + } + } + if(!retval) +#endif + { + int fd2 = fd; + FILE *f = 0; + if (fd == 0 && !closeme + && (!mode || mode[0]=='r') ) + { + f=stdin; + default_mode = "r"; + fd2=(-1); + } + else if (fd == 1 && !closeme + && (!mode || mode[0]=='a' || mode[0]=='w') ) + { + default_mode = "a"; + f=stdout; + fd2 = -1; + } + else if (fd == 2 && !closeme + && (!mode || mode[0]=='a' || mode[0]=='w') ) + { + default_mode = "a"; + f=stderr; + fd2 = -1; + } + else + { + if (! closeme) + fd2 = dup(fd); + f = fdopen(fd2,(char*)(mode?mode:default_mode)); + } + + if(!f) + { + if ( fd2 >= 0) + close(fd2); + G_THROW( ERR_MSG("ByteStream.open_fail2") ); + } + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f,mode?mode:default_mode,(fd2>=0)); + if(errmessage.length()) + G_THROW(errmessage); + } + return retval; +} + +GP<ByteStream> +ByteStream::create(FILE * const f,char const * const mode,const bool closeme) +{ + GP<ByteStream> retval; +#if HAS_MEMMAP + if (!mode || (GUTF8String("rb") == mode)) + { + MemoryMapByteStream *rb=new MemoryMapByteStream(); + retval=rb; + GUTF8String errmessage=rb->init(fileno(f),false); + if(errmessage.length()) + { + retval=0; + }else + { + fclose(f); + } + } + if(!retval) +#endif + { + Stdio *sbs=new Stdio(); + retval=sbs; + GUTF8String errmessage=sbs->init(f,mode?mode:"rb",closeme); + if(errmessage.length()) + { + G_THROW(errmessage); + } + } + return retval; +} + +GP<ByteStream> +ByteStream::create_static(const void * const buffer, size_t sz) +{ + return new Static(buffer, sz); +} + +GP<ByteStream> +ByteStream::duplicate(const size_t xsize) const +{ + GP<ByteStream> retval; + const long int pos=tell(); + const int tsize=size(); + ByteStream &self=*(const_cast<ByteStream *>(this)); + if(tsize < 0 || pos < 0 || (unsigned int)tsize < 1+(unsigned int)pos) + { + retval=ByteStream::create(); + retval->copy(self,xsize); + retval->seek(0L); + }else + { + const size_t s=(size_t)tsize-(size_t)pos; + const int size=(!xsize||(s<xsize))?s:xsize; + ByteStream::Static::Allocate *bs=new ByteStream::Static::Allocate(size); + retval=bs; + self.readall(bs->buf,size); + } + self.seek(pos,SEEK_SET,true); + return retval; +} + + +#if HAS_MEMMAP +MemoryMapByteStream::MemoryMapByteStream(void) +: ByteStream::Static(0,0) +{} + +GUTF8String +MemoryMapByteStream::init(FILE *const f,const bool closeme) +{ + GUTF8String retval; + retval=init(fileno(f),false); + if(closeme) + { + fclose(f); + } + return retval; +} + +GUTF8String +MemoryMapByteStream::init(const int fd,const bool closeme) +{ + GUTF8String retval; +#if defined(PROT_READ) && defined(MAP_SHARED) + struct stat statbuf; + if(!fstat(fd,&statbuf)) + { + if(statbuf.st_size) + { + bsize=statbuf.st_size; + data=(char *)mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fd,0); + } + }else + { + if(closeme) + { + close(fd); + } + retval= ERR_MSG("ByteStream.open_fail2"); + } +#else + retval= ERR_MSG("ByteStream.open_fail2"); +#endif + if(closeme) + { + close(fd); + } + return retval; +} + +MemoryMapByteStream::~MemoryMapByteStream() +{ + if(data) + { + munmap(const_cast<char *>(data),bsize); + } +} + +#endif + +ByteStream::Wrapper::~Wrapper() {} + + +GP<ByteStream> +ByteStream::get_stdin(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(0,mode,false); + return gp; +} + +GP<ByteStream> +ByteStream::get_stdout(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(1,mode,false); + return gp; +} + +GP<ByteStream> +ByteStream::get_stderr(char const * const mode) +{ + static GP<ByteStream> gp = ByteStream::create(2,mode,false); + return gp; +} + + +/** Looks up the message and writes it to the specified stream. */ +void ByteStream::formatmessage( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + writemessage( message ); +} + +/** Looks up the message and writes it to the specified stream. */ +void ByteStream::writemessage( const char *message ) +{ + writestring( DjVuMessage::LookUpUTF8( message ) ); +} + +static void +read_file(ByteStream &bs,char *&buffer,GPBuffer<char> &gbuffer) +{ + const int size=bs.size(); + int pos=0; + if(size>0) + { + size_t readsize=size+1; + gbuffer.resize(readsize); + for(int i;readsize&&(i=bs.read(buffer+pos,readsize))>0;pos+=i,readsize-=i) + EMPTY_LOOP; + }else + { + const size_t readsize=32768; + gbuffer.resize(readsize); + for(int i;((i=bs.read(buffer+pos,readsize))>0); + gbuffer.resize((pos+=i)+readsize)) + EMPTY_LOOP; + } + buffer[pos]=0; +} + +GNativeString +ByteStream::getAsNative(void) +{ + char *buffer; + GPBuffer<char> gbuffer(buffer); + read_file(*this,buffer,gbuffer); + return GNativeString(buffer); +} + +GUTF8String +ByteStream::getAsUTF8(void) +{ + char *buffer; + GPBuffer<char> gbuffer(buffer); + read_file(*this,buffer,gbuffer); + return GUTF8String(buffer); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuPrintErrorUTF8(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + errout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + errout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintErrorNative(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + errout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + errout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintMessageUTF8(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + strout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + strout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuPrintMessageNative(const char *fmt, ... ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + strout->cp=ByteStream::NATIVE; + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + strout->writestring(message); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} diff --git a/kviewshell/plugins/djvu/libdjvu/ByteStream.h b/kviewshell/plugins/djvu/libdjvu/ByteStream.h new file mode 100644 index 00000000..7ecfd8b7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ByteStream.h @@ -0,0 +1,416 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ByteStream.h,v 1.11 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _BYTESTREAM_H +#define _BYTESTREAM_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name ByteStream.h + + Files #"ByteStream.h"# and #"ByteStream.cpp"# define input/output classes + similar in spirit to the well known C++ #iostream# classes. Class + \Ref{ByteStream} is an abstract base class for all byte streams. It + defines a virtual interface and also provides useful functions. These + files provide two subclasses. Class \Ref{ByteStream::Stdio} provides a + simple interface to the Ansi C buffered input/output functions. Class + \Ref{ByteStream::Memory} provides stream-like access to a dynamical array + maintained in memory. Class \Ref{ByteStream::Static} provides read-only + stream-like access to a user allocated data buffer. + + {\bf Notes} --- These classes were partly written because we did not want to + depend on the standard C++ library. The main reason however is related to + the browser interface. We want to have a tight control over the + implementation of subclasses because we want to use a byte stream to + represent data passed by a web browser to a plugin. This operation + involves multi-threading issues that many implementations of the standard + C++ library would squarely ignore. + + @memo + Input/output classes + @author + L\'eon Bottou <[email protected]> -- initial implementation\\ + Andrei Erofeev <[email protected]> -- + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + + + @version + #$Id: ByteStream.h,v 1.11 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "Arrays.h" +#include <stdio.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GURL; +class GUTF8String; +class GNativeString; + +/** Abstract class for a stream of bytes. Class #ByteStream# represent an + object from which (resp. to which) bytes can be read (resp. written) as + with a regular file. Virtual functions #read# and #write# must implement + these two basic operations. In addition, function #tell# returns an + offset identifying the current position, and function #seek# may be used + to change the current position. + + {\bf Note}. Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. +*/ +class ByteStream : public GPEnabled +{ +public: + class Stdio; + class Static; + class Memory; + class Wrapper; + enum codepage_type {RAW,AUTO,NATIVE,UTF8} cp; + + /** @name Virtual Functions. + These functions are usually implemented by each subclass of #ByteStream#. + */ + //@{ +public: + /** Virtual destructor. */ + virtual ~ByteStream(); + /** Reads data from a ByteStream. This function {\em must} be implemented + by each subclass of #ByteStream#. At most #size# bytes are read from + the ByteStream and stored in the memory area pointed to by #buffer#. + Function #read# returns immediately if #size# is zero. The actual number + of bytes read is returned. Function #read# returns a number of bytes + smaller than #size# if the end-of-file mark is reached before filling + the buffer. Subsequent invocations will always return value #0#. + Function #read# may also return a value greater than zero but smaller + than #size# for internal reasons. Programs must be ready to handle these + cases or use function \Ref{readall}. Exception \Ref{GException} is + thrown with a plain text error message whenever an error occurs. */ + virtual size_t read(void *buffer, size_t size); + /** Writes data to a ByteStream. This function {\em must} be implemented by + each subclass of #ByteStream#. At most #size# bytes from buffer + #buffer# are written to the ByteStream. Function #write# returns + immediately if #size# is zero. The actual number of bytes written is + returned. Function #write# may also return a value greater than zero but + smaller than #size# for internal reasons. Programs must be ready to + handle these cases or use function \Ref{writall}. Exception + \Ref{GException} is thrown with a plain text error message whenever an + error occurs. */ + virtual size_t write(const void *buffer, size_t size); + /** Returns the offset of the current position in the ByteStream. This + function {\em must} be implemented by each subclass of #ByteStream#. */ + virtual long tell(void) const = 0; + /** Sets the current position for reading or writing the ByteStream. Class + #ByteStream# provides a default implementation able to seek forward by + calling function #read# until reaching the desired position. Subclasses + implementing better seek capabilities must override this default + implementation. The new current position is computed by applying + displacement #offset# to the position represented by argument + #whence#. The following values are recognized for argument #whence#: + \begin{description} + \item[#SEEK_SET#] Argument #offset# indicates the position relative to + the beginning of the ByteStream. + \item[#SEEK_CUR#] Argument #offset# is a signed displacement relative to + the current position. + \item[#SEEK_END#] Argument #offset# is a displacement relative to the end + of the file. It is then advisable to provide a negative value for #offset#. + \end{description} + Results are undefined whenever the new position is greater than the + total size of the ByteStream. + + {\bf Error reporting}: + If #seek()# succeeds, #0# is returned. Otherwise it either returns + #-1# (if #nothrow# is set to #FALSE#) or throws the \Ref{GException} + exception. */ + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + /** Flushes all buffers in the ByteStream. Calling this function + guarantees that pending data have been actually written (i.e. passed to + the operating system). Class #ByteStream# provides a default + implementation which does nothing. */ + virtual void flush(void); + //@} + /** @name Utility Functions. + Class #ByteStream# implements these functions using the virtual + interface functions only. All subclasses of #ByteStream# inherit these + functions. */ + //@{ +public: + /** Reads data and blocks until everything has been read. This function is + essentially similar to function #read#. Unlike function #read# however, + function #readall# will never return a value smaller than #size# unless + an end-of-file mark is reached. This is implemented by repeatedly + calling function #read# until everything is read or until we reach an + end-of-file mark. Note that #read# and #readall# are equivalent when + #size# is one. */ + size_t readall(void *buffer, size_t size); + /** Writes data and blocks until everything has been written. This function + is essentially similar to function #write#. Unlike function #write# + however, function #writall# will only return after all #size# bytes have + been written. This is implemented by repeatedly calling function + #write# until everything is written. Note that #write# and #writall# + are equivalent when #size# is one. */ + size_t writall(const void *buffer, size_t size); + /** Copy data from another ByteStream. A maximum of #size# bytes are read + from the ByteStream #bsfrom# and are written to the ByteStream #*this# + at the current position. Less than #size# bytes may be written if an + end-of-file mark is reached on #bsfrom#. This function returns the + total number of bytes copied. Setting argument #size# to zero (the + default value) has a special meaning: the copying process will continue + until reaching the end-of-file mark on ByteStream #bsfrom#, regardless + of the number of bytes transferred. */ + size_t copy(ByteStream &bsfrom, size_t size=0); + /** Create a new #ByteStream# that copies the data from this #ByteStream# + starting from the current position, upto #size# bytes. Setting the + #size# to zero means copy to the end-of-file mark. */ + GP<ByteStream> duplicate(const size_t size=0) const; + /// Allows printf() type operations to a bytestream. + size_t format(const char *fmt, ... ); + /// Allows scanf() type operations on a bytestream. + int scanf(const char *fmt, ... ); + /** Writes the string as is, to the specified stream. */ + size_t writestring(const GUTF8String &s); + /** Writes the string as is, to the specified stream. */ + size_t writestring(const GNativeString &s); + /** Formats the message string, looks up the external representation + and writes it to the specified stream. */ + void formatmessage( const char *fmt, ... ); + /** Looks up the message and writes it to the specified stream. */ + void writemessage( const char *message ); + /** Writes a one-byte integer to a ByteStream. */ + void write8 (unsigned int card8); + /** Writes a two-bytes integer to a ByteStream. + The integer most significant byte is written first, + regardless of the processor endianness. */ + void write16(unsigned int card16); + /** Writes a three-bytes integer to a ByteStream. + The integer most significant byte is written first, + regardless of the processor endianness. */ + void write24(unsigned int card24); + /** Writes a four-bytes integer to a ByteStream. + The integer most significant bytes are written first, + regardless of the processor endianness. */ + void write32(unsigned int card32); + /** Reads a one-byte integer from a ByteStream. */ + unsigned int read8 (); + /** Reads a two-bytes integer from a ByteStream. + The integer most significant byte is read first, + regardless of the processor endianness. */ + unsigned int read16(); + /** Reads a three-bytes integer from a ByteStream. + The integer most significant byte is read first, + regardless of the processor endianness. */ + unsigned int read24(); + /** Reads a four-bytes integer from a ByteStream. + The integer most significant bytes are read first, + regardless of the processor endianness. */ + unsigned int read32(); + /** Returns the total number of bytes contained in the buffer, file, etc. + Valid offsets for function #seek# range from 0 to the value returned + by this function. */ + virtual int size(void) const; + /// Use at your own risk, only guarenteed to work for ByteStream::Memorys. + TArray<char> get_data(void); + /** Reads data from a random position. This function reads at most #sz# + bytes at position #pos# into #buffer# and returns the actual number of + bytes read. The current position is unchanged. */ + virtual size_t readat(void *buffer, size_t sz, int pos); + /// Returns false, unless a subclass of ByteStream::Static + virtual bool is_static(void) const { return false; } + //@} +protected: + ByteStream(void) : cp(AUTO) {}; +private: + // Cancel C++ default stuff + ByteStream(const ByteStream &); + ByteStream & operator=(const ByteStream &); +public: + /** Constructs an empty Memory ByteStream. The buffer itself is organized + as an array of 4096 byte blocks. The buffer is initially empty. You + must first use function #write# to store data into the buffer, use + function #seek# to rewind the current position, and function #read# to + read the data back. */ + static GP<ByteStream> create(void); + /** Constructs a Memory ByteStream by copying initial data. The + Memory buffer is initialized with #size# bytes copied from the + memory area pointed to by #buffer#. */ + static GP<ByteStream> create(void const * const buffer, const size_t size); + /** Constructs a ByteStream for accessing the file named #url#. + Arguments #url# and #mode# are similar to the arguments of the well + known stdio function #fopen#. In addition a url of #-# will be + interpreted as the standard output or the standard input according to + #mode#. This constructor will open a stdio file and construct a + ByteStream object accessing this file. Destroying the ByteStream object + will flush and close the associated stdio file. Exception + \Ref{GException} is thrown with a plain text error message if the stdio + file cannot be opened. */ + static GP<ByteStream> create( + const GURL &url, char const * const mode); + /** Same as the above, but uses stdin or stdout */ + static GP<ByteStream> create( char const * const mode); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + static GP<ByteStream> create( + const int fd, char const * const mode, const bool closeme); + + /** Constructs a ByteStream for accessing the stdio file #f#. + Argument #mode# indicates the type of the stdio file, as in the + well known stdio function #fopen#. Destroying the ByteStream + object will not close the stdio file #f# unless closeme is true. */ + static GP<ByteStream> create( + FILE * const f, char const * const mode, const bool closeme); + /** Creates a ByteStream object for allocating the memory area of + length #sz# starting at address #buffer#. This call impliments + a read-only ByteStream interface for a memory area specified by + the user at construction time. Calls to function #read# directly + access this memory area. The user must therefore make sure that its + content remain valid long enough. */ + static GP<ByteStream> create_static( + void const * const buffer, const size_t size); + + /** Easy access to preallocated stdin/stdout/stderr bytestreams */ + static GP<ByteStream> get_stdin(char const * const mode=0); + static GP<ByteStream> get_stdout(char const * const mode=0); + static GP<ByteStream> get_stderr(char const * const mode=0); + + /** This is the conventional name for EOF exceptions */ + static const char *EndOfFile; + /** Returns the contents of the file as a GNativeString */ + GNativeString getAsNative(void); + /** Returns the contents of the file as a GUTF8String */ + GUTF8String getAsUTF8(void); +}; + +inline size_t +ByteStream::readat(void *buffer, size_t sz, int pos) +{ + size_t retval; + long tpos=tell(); + seek(pos, SEEK_SET, true); + retval=readall(buffer,sz); + seek(tpos, SEEK_SET, true); + return retval; +} + +inline int +ByteStream::size(void) const +{ + ByteStream *bs=const_cast<ByteStream *>(this); + int bsize=(-1); + long pos=tell(); + if(bs->seek(0,SEEK_END,true)) + { + bsize=(int)tell(); + (void)(bs->seek(pos,SEEK_SET,false)); + } + return bsize; +} + +/** ByteStream::Wrapper implements wrapping bytestream. This is useful + for derived classes that take a GP<ByteStream> as a creation argument, + and the backwards compatible bytestreams. */ +class ByteStream::Wrapper : public ByteStream +{ +protected: + GP<ByteStream> gbs; + ByteStream *bs; + Wrapper(void) : bs(0) {} + Wrapper(const GP<ByteStream> &xbs) : gbs(xbs), bs(xbs) {} +public: + ~Wrapper(); + ByteStream * operator & () const {return bs;} + ByteStream * operator & () {return bs;} + virtual size_t read(void *buffer, size_t size) + { return bs->read(buffer,size); } + virtual size_t write(const void *buffer, size_t size) + { return bs->write(buffer,size); } + virtual long tell(void) const + { return bs->tell(); } + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false) + { return bs->seek(offset,whence,nothrow); } + virtual void flush(void) + { bs->flush(); } +}; + + +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DataPool.cpp b/kviewshell/plugins/djvu/libdjvu/DataPool.cpp new file mode 100644 index 00000000..1190292e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DataPool.cpp @@ -0,0 +1,1837 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DataPool.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DataPool.h" +#include "IFFByteStream.h" +#include "GString.h" +#include "GOS.h" +#include "GURL.h" +#include "debug.h" + +#ifndef macintosh +# include <sys/types.h> +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +const char * DataPool::Stop = ERR_MSG("STOP"); + +static void +// call_callback(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +call_callback(void (* callback)(void *), void *cl_data) +{ + G_TRY + { + if (callback) + callback(cl_data); + } G_CATCH_ALL {} G_ENDCATCH; +} + + +//**************************************************************************** +//****************************** OpenFiles *********************************** +//**************************************************************************** + +#define MAX_OPEN_FILES 15 + +/** The purpose of this class is to limit the number of files open by + connected DataPools. Now, when a DataPool is connected to a file, it + doesn't necessarily has it open. Every time it needs access to data + it's supposed to ask this file for the ByteStream. It should + also inform the class when it's going to die (so that the file can + be closed). OpenFiles makes sure, that the number of open files + doesn't exceed MAX_OPEN_FILES. When it does, it looks for the oldest + file, closes it and asks all DataPools working with it to ZERO + their GP<> pointers. */ +class DataPool::OpenFiles_File : public GPEnabled +{ +public: + GURL url; + GP<ByteStream> stream; // Stream connected to 'url' + GCriticalSection stream_lock; + GPList<DataPool> pools_list; // List of pools using this stream + GCriticalSection pools_lock; + unsigned long open_time; // Time when stream was open + + int add_pool(GP<DataPool> &pool); + int del_pool(GP<DataPool> &pool); + + OpenFiles_File(const GURL &url, GP<DataPool> &pool); + virtual ~OpenFiles_File(void); + void clear_stream(void); +}; + +class DataPool::OpenFiles : public GPEnabled +{ +private: + static OpenFiles * global_ptr; + + GPList<DataPool::OpenFiles_File> files_list; + GCriticalSection files_lock; +public: + static OpenFiles * get(void); + + // Opend the specified file if necessary (or finds an already open one) + // and returns it. The caller (pool) is stored in the list associated + // with the stream. Whenever OpenFiles decides, that this stream + // had better be closed, it will order every pool from the list to + // ZERO their references to it + GP<DataPool::OpenFiles_File> request_stream(const GURL &url, GP<DataPool> pool); + // If there are more than MAX_STREAM_FILES open, close the oldest. + void prune(void); + // Removes the pool from the list associated with the stream. + // If there is nobody else using this stream, the stream will + // be closed too. + void stream_released(GP<ByteStream> &stream, GP<DataPool> pool); + + void close_all(void); +}; + +DataPool::OpenFiles * DataPool::OpenFiles::global_ptr; + +DataPool::OpenFiles_File::OpenFiles_File(const GURL &xurl, GP<DataPool> &pool) : url(xurl) +{ + DEBUG_MSG("DataPool::OpenFiles_File::OpenFiles_File(): Opening file '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + open_time=GOS::ticks(); + stream=ByteStream::create(url,"rb"); + add_pool(pool); +} + +DataPool::OpenFiles_File::~OpenFiles_File(void) +{ + DEBUG_MSG("DataPool::OpenFiles_File::~OpenFiles_File(): Closing file '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + clear_stream(); +} + +void +DataPool::OpenFiles_File::clear_stream(void) +{ + GCriticalSectionLock lock(&pools_lock); + for(GPosition pos=pools_list;pos;++pos) + if(pools_list[pos]) + pools_list[pos]->clear_stream(false); + pools_list.empty(); +} + +int +DataPool::OpenFiles_File::add_pool(GP<DataPool> &pool) +{ + DEBUG_MSG("DataPool::OpenFiles_File::add_pool: pool=" << (void *) pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&pools_lock); + if (!pools_list.contains(pool)) + pools_list.append(pool); + return pools_list.size(); +} + +int +DataPool::OpenFiles_File::del_pool(GP<DataPool> &pool) +{ + DEBUG_MSG("DataPool::OpenFiles_File::del_pool: pool=" << (void *) pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&pools_lock); + GPosition pos; + if (pools_list.search(pool, pos)) + pools_list.del(pos); + return pools_list.size(); +} + +inline DataPool::OpenFiles * +DataPool::OpenFiles::get(void) +{ + DEBUG_MSG("DataPool::OpenFiles::get()\n"); + DEBUG_MAKE_INDENT(3); + if (!global_ptr) + global_ptr=new OpenFiles(); + return global_ptr; +} + +void +DataPool::OpenFiles::prune(void) +{ + DEBUG_MSG("DataPool::OpenFiles::prune(void): "<<files_list.size()<< "\n"); + DEBUG_MAKE_INDENT(3); + while(files_list.size()>MAX_OPEN_FILES) + { + // Too many open files (streams). Get rid of the oldest one. + unsigned long oldest_time=GOS::ticks(); + GPosition oldest_pos=files_list; + for(GPosition pos=files_list;pos;++pos) + { + if (files_list[pos]->open_time<oldest_time) + { + oldest_time=files_list[pos]->open_time; + oldest_pos=pos; + } + } + files_list[oldest_pos]->clear_stream(); + files_list.del(oldest_pos); + } +} + +// GP<ByteStream> & stream, +// GCriticalSection ** stream_lock) +GP<DataPool::OpenFiles_File> +DataPool::OpenFiles::request_stream(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("DataPool::OpenFiles::request_stream(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool::OpenFiles_File> file; + + // Check: maybe the stream has already been open by request of + // another DataPool + GCriticalSectionLock lock(&files_lock); + for(GPosition pos=files_list;pos;++pos) + { + if (files_list[pos]->url==url) + { + DEBUG_MSG("found existing stream\n"); + file=files_list[pos]; + break; + } + } + + // No? Open the stream, but check, that there are not + // too many streams open + if (!file) + { + file=new DataPool::OpenFiles_File(url, pool); + files_list.append(file); + prune(); + } + + file->add_pool(pool); + return file; +} + +void +DataPool::OpenFiles::stream_released(GP<ByteStream> &stream, GP<DataPool> pool) +{ + DEBUG_MSG("DataPool::OpenFiles::stream_release: stream=" + << (void *)stream << " pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&files_lock); + for(GPosition pos=files_list;pos;) + { + GPosition dpos = pos; + ++pos; + GP<DataPool::OpenFiles_File> f=files_list[dpos]; + if ((ByteStream *)(f->stream) == (ByteStream *)stream) + if (f->del_pool(pool)==0) + files_list.del(dpos); + } +} + +// This isn't really an accurate url. The files are not really +// closed. Instead they are dereferenced from the data pool. If +// a there is another reference to the respective bytestream, it +// will remain open until dereferenced. +void +DataPool::OpenFiles::close_all(void) +{ + DEBUG_MSG("DataPool::OpenFiles::close_all\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&files_lock); + files_list.empty(); +} + +//**************************************************************************** +//******************************** FCPools *********************************** +//**************************************************************************** + +/** This class is used to maintain a list of DataPools connected to a file. + It's important to have this list if we want to do something with this file + like to modify it or just erase. Since any modifications of the file + will break DataPools directly connected to it, it would be nice to have + a mechanism for signaling all the related DataPools to read data into + memory. This is precisely the purpose of this class. */ +class FCPools +{ +private: + GMap<GURL, GPList<DataPool> > map; // GMap<GUTF8String, GPList<DataPool>> in fact + GCriticalSection map_lock; + + static FCPools * global_ptr; +public: + static FCPools * get(void); + // Adds the <furl, pool> pair into the list + void add_pool(const GURL &furl, GP<DataPool> pool); + // Removes the <furl, pool> pair from the list + void del_pool(const GURL &furl, GP<DataPool> pool); + // Looks for the list of DataPools connected to 'furl' and makes + // each of them load the contents of the file into memory + void load_file(const GURL &url); + // Retrieve a local URL, if available. + GP<DataPool> get_pool(const GURL &url, int start, int length); + void clean(void); +}; + +void +FCPools::clean(void) +{ + GCriticalSectionLock lock(&map_lock); + static int count=0; + if(! count++) + { + bool restart = true; + while (restart) + { + restart = false; + for (GPosition posmap = map; posmap; ++posmap) + { + GPList<DataPool> *lst; + lst = & map[posmap]; + if (lst->isempty()) + { + map.del(posmap); + restart = true; + break; + } + for (GPosition poslst = *lst; poslst; ++poslst) + if ((*lst)[poslst]->get_count() < 2) + { + lst->del(poslst); + restart = true; + break; + } + if (restart) + break; + } + } + } + --count; +} + +void +FCPools::add_pool(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("FCPools::add_pool: url='" << url << "' pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + if (url.is_local_file_url()) + { + GPList<DataPool> list; + GPosition pos(map.contains(url)); + if (! pos) + { + map[url]=list; + pos=map.contains(url); + } + GPList<DataPool> &plist=map[pos]; + if (!plist.contains(pool)) + plist.append(pool); + } + clean(); +} + +GP<DataPool> +FCPools::get_pool(const GURL &url, int start, int length) +{ + DEBUG_MSG("FCPools::get_pool: url='" << url << "\n"); + DEBUG_MAKE_INDENT(3); + GP<DataPool> retval; + if (url.is_local_file_url()) + { + GCriticalSectionLock lock(&map_lock); + GPosition pos(map.contains(url)); + if (pos) + { + GPList<DataPool> &plist=map[pos]; + for(pos=plist;pos;++pos) + { + DataPool &pool=*plist[pos]; + if(start == pool.start && (length < 0 || (length == pool.length))) + { + retval=plist[pos]; + break; + } + } + } + clean(); + } + return retval; +} + +void +FCPools::del_pool(const GURL &url, GP<DataPool> pool) +{ + DEBUG_MSG("FCPools::del_pool: url='" << url << "' pool=" << (void *)pool << "\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + clean(); + if (url.is_local_file_url()) + { + GPosition pos; + if (map.contains(url, pos)) + { + GPList<DataPool> &list=map[pos]; + GPosition list_pos; + while(list.search(pool, list_pos)) + list.del(list_pos); + if (list.isempty()) + { + map.del(pos); + } + } + } +} + +void +FCPools::load_file(const GURL &url) +{ + DEBUG_MSG("FCPools::load_file: url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lock(&map_lock); + + clean(); + if (url.is_local_file_url()) + { + GPosition pos; + if (map.contains(url, pos)) + { + // We make here a copy of the list because DataPool::load_file() + // will call FCPools::del_pool(), which will modify the list + GPList<DataPool> list=map[pos]; + for(GPosition list_pos=list;list_pos;++list_pos) + list[list_pos]->load_file(); + } + } +} + +FCPools * FCPools::global_ptr; + +inline FCPools * +FCPools::get(void) +{ + if (!global_ptr) + global_ptr=new FCPools(); + return global_ptr; +} + +//**************************************************************************** +//****************************** BlockList *********************************** +//**************************************************************************** + +// Since data can be added to the DataPool at any offset now, there may +// be white spots, which contain illegal data. This class is to contain +// the list of valid and invalid regions. +// The class is basically a list of integers. Abs(integer)=size of the +// block. If the integer is positive, data for the block is known. +// Otherwise it's unkown. + +class DataPool::BlockList +{ + // See comments in .cpp file. +private: + GCriticalSection lock; + GList<int> list; +public: + BlockList() {}; + void clear(void); + void add_range(int start, int length); + int get_bytes(int start, int length) const; + int get_range(int start, int length) const; +friend class DataPool; +}; + +void +DataPool::BlockList::clear(void) +{ + DEBUG_MSG("DataPool::BlockList::clear()\n"); + DEBUG_MAKE_INDENT(3); + GCriticalSectionLock lk(&lock); + list.empty(); +} + +void +DataPool::BlockList::add_range(int start, int length) + // Adds range of known data. +{ + DEBUG_MSG("DataPool::BlockList::add_range: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + if (start<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + if (length<=0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + if (length>0) + { + GCriticalSectionLock lk(&lock); + + // Look thru existing zones, change their sign and split if + // necessary. + GPosition pos=list; + int block_start=0, block_end=0; + while(pos && block_start<start+length) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (size<0) + if (block_start<start) + { + if (block_end>start && block_end<=start+length) + { + list[pos]=-(start-block_start); + list.insert_after(pos, block_end-start); + ++pos; + block_start=start; + } else if (block_end>start+length) + { + list[pos]=-(start-block_start); + list.insert_after(pos, length); + ++pos; + list.insert_after(pos, -(block_end-(start+length))); + ++pos; + block_start=start+length; + } + } else if (block_start>=start && block_start<start+length) + { + if (block_end<=start+length) list[pos]=abs(size); + else + { + list[pos]=start+length-block_start; + list.insert_after(pos, -(block_end-(start+length))); + ++pos; + block_start=start+length; + } + } + block_start=block_end; + ++pos; + } + if (block_end<start) + { + list.append(-(start-block_end)); + list.append(length); + } else if (block_end<start+length) list.append(start+length-block_end); + + // Now merge adjacent areas with the same sign + pos=list; + while(pos) + { + GPosition pos1=pos; ++pos1; + while(pos1) + { + if (list[pos]<0 && list[pos1]>0 || + list[pos]>0 && list[pos1]<0) + break; + list[pos]+=list[pos1]; + GPosition this_pos=pos1; + ++pos1; + list.del(this_pos); + } + pos=pos1; + } + } // if (length>0) +} + +int +DataPool::BlockList::get_bytes(int start, int length) const + // Returns the number of bytes of data available in the range + // [start, start+length[. There may be holes between data chunks +{ + DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + + if (length<0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + + GCriticalSectionLock lk((GCriticalSection *) &lock); + int bytes=0; + int block_start=0, block_end=0; + for(GPosition pos=list;pos && block_start<start+length;++pos) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (size>0) + if (block_start<start) + { + if (block_end>=start && block_end<start+length) + bytes+=block_end-start; + else if (block_end>=start+length) + bytes+=length; + } else + { + if (block_end<=start+length) + bytes+=block_end-block_start; + else bytes+=start+length-block_start; + } + block_start=block_end; + } + return bytes; +} + +int +DataPool::BlockList::get_range(int start, int length) const + // Finds a range covering offset=start and returns the length + // of intersection of this range with [start, start+length[ + // 0 is returned if nothing can be found +{ + DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n"); + DEBUG_MAKE_INDENT(3); + if (start<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + if (length<=0) + G_THROW( ERR_MSG("DataPool.bad_length") ); + + GCriticalSectionLock lk((GCriticalSection *) &lock); + int block_start=0, block_end=0; + for(GPosition pos=list;pos && block_start<start+length;++pos) + { + int size=list[pos]; + block_end=block_start+abs(size); + if (block_start<=start && block_end>start) + if (size<0) return -1; + else + if (block_end>start+length) return length; + else return block_end-start; + block_start=block_end; + } + return 0; +} + +//**************************************************************************** +//******************************* DataPool *********************************** +//**************************************************************************** + +class DataPool::Reader : public GPEnabled +{ +public: + GEvent event; + bool reenter_flag; + int offset; + int size; + Reader() : reenter_flag(false), offset(0), size(-1){}; + Reader(int offset_in, int size_in=-1) : + reenter_flag(false), offset(offset_in), size(size_in) {}; + virtual ~Reader() {}; +}; + +class DataPool::Trigger : public GPEnabled +{ +public: + GSafeFlags disabled; + int start, length; +// void (* callback)(GP<GPEnabled> &); + void (* callback)(void *); +// GP<GPEnabled> cl_data; + void *cl_data; + + Trigger() : start(0), length(-1), callback(0), cl_data(0) {}; + Trigger(int xstart, int xlength, +// void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) : + void (* xcallback)(void *), void *xcl_data) : + start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {}; + virtual ~Trigger() {}; +}; + +class DataPool::Counter +{ +private: + int counter; + GCriticalSection lock; +public: + Counter() : counter(0) {}; + operator int(void) const; + void inc(void); + void dec(void); +}; + +#define DATAPOOL_INIT eof_flag(false),stop_flag(false), \ + stop_blocked_flag(false), \ + add_at(0),start(0),length(-1) + +void +DataPool::init(void) +{ + DEBUG_MSG("DataPool::init(): Initializing\n"); + DEBUG_MAKE_INDENT(3); + start=0; length=-1; add_at=0; + eof_flag=false; + stop_flag=false; + stop_blocked_flag=false; + + active_readers=new Counter; + block_list=0; + G_TRY + { + block_list=new BlockList; + data=ByteStream::create(); + } + G_CATCH_ALL + { + delete block_list; + block_list=0; + delete active_readers; + active_readers=0; + G_RETHROW; + } + G_ENDCATCH; +} + +DataPool::DataPool(void) : DATAPOOL_INIT {} + +GP<DataPool> +DataPool::create(void) +{ + DEBUG_MSG("DataPool::DataPool()\n"); + DEBUG_MAKE_INDENT(3); + DataPool *pool=new DataPool(); + + GP<DataPool> retval=pool; + pool->init(); + + // If we maintain the data ourselves, we want to interpret its + // IFF structure to predict its length + pool->add_trigger(0, 32, static_trigger_cb, pool); + return retval; +} + +GP<DataPool> +DataPool::create(const GP<ByteStream> &gstr) +{ + DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n"); + DEBUG_MAKE_INDENT(3); + DataPool *pool=new DataPool(); + GP<DataPool> retval=pool; + pool->init(); + + // It's nice to have IFF data analyzed in this case too. + pool->add_trigger(0, 32, static_trigger_cb, pool); + + pool->data=gstr->duplicate(); + pool->added_data(0,pool->data->size()); +// char buffer[1024]; +// int length; +// while((length=str.read(buffer, 1024))) +// pool->add_data(buffer, length); + pool->set_eof(); + return retval; +} + +GP<DataPool> +DataPool::create(const GP<DataPool> & pool, int start, int length) +{ + DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n"); + DEBUG_MAKE_INDENT(3); + + DataPool *xpool=new DataPool(); + GP<DataPool> retval=xpool; + xpool->init(); + xpool->connect(pool, start, length); + return retval; +} + +GP<DataPool> +DataPool::create(const GURL &furl, int start, int length) +{ + DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length); + if(! retval) + { + DataPool *pool=new DataPool(); + retval=pool; + pool->init(); + pool->connect(furl, start, length); + } + return retval; +} + +void +DataPool::clear_stream(const bool release) +{ + DEBUG_MSG("DataPool::clear_stream()\n"); + DEBUG_MAKE_INDENT(3); + if(fstream) + { + GCriticalSectionLock lock1(&class_stream_lock); + GP<OpenFiles_File> f=fstream; + if(f) + { + GCriticalSectionLock lock2(&(f->stream_lock)); + fstream=0; + if(release) + OpenFiles::get()->stream_released(f->stream, this); + } + } +} + +DataPool::~DataPool(void) +{ + DEBUG_MSG("DataPool::~DataPool()\n"); + DEBUG_MAKE_INDENT(3); + + clear_stream(true); + if (furl.is_local_file_url()) + { + FCPools::get()->del_pool(furl, this); + } + + { + // Wait until the static_trigger_cb() exits + GCriticalSectionLock lock(&trigger_lock); + if (pool) + pool->del_trigger(static_trigger_cb, this); + del_trigger(static_trigger_cb, this); + } + + if (pool) + { + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> trigger=triggers_list[pos]; + pool->del_trigger(trigger->callback, trigger->cl_data); + } + } + delete block_list; + delete active_readers; +} + +void +DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in) +{ + DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) G_THROW( ERR_MSG("DataPool.connected1") ); + if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") ); + if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") ); + + pool=pool_in; + start=start_in; + length=length_in; + + // The following will work for length<0 too + if (pool->has_data(start, length)) + eof_flag=true; + else + pool->add_trigger(start, length, static_trigger_cb, this); + + data=0; + + wake_up_all_readers(); + + // Pass registered trigger callbacks to the DataPool + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + int tlength=t->length; + if (tlength<0 && length>0) + tlength=length-t->start; + pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data); + } +} + +void +DataPool::connect(const GURL &furl_in, int start_in, int length_in) +{ + DEBUG_MSG("DataPool::connect(): connecting to a file\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + G_THROW( ERR_MSG("DataPool.connected1") ); + if (furl.is_local_file_url()) + G_THROW( ERR_MSG("DataPool.connected2") ); + if (start_in<0) + G_THROW( ERR_MSG("DataPool.neg_start") ); + + + if (furl_in.name() == "-") + { + DEBUG_MSG("This is stdin => just read the data...\n"); + DEBUG_MAKE_INDENT(3); + char buffer[1024]; + int length; + GP<ByteStream> gstr=ByteStream::create(furl_in, "rb"); + ByteStream &str=*gstr; + while((length=str.read(buffer, 1024))) + add_data(buffer, length); + set_eof(); + } else if(furl_in.is_local_file_url()) + { + // Open the stream (just in this function) too see if + // the file is accessible. In future we will be using 'OpenFiles' + // to request and release streams + GP<ByteStream> str=ByteStream::create(furl_in,"rb"); + str->seek(0, SEEK_END); + int file_size=str->tell(); + + furl=furl_in; + start=start_in; + length=length_in; + if (start>=file_size) + length=0; + else if (length<0 || start+length>=file_size) + length=file_size-start; + + eof_flag=true; + + if(str->is_static()) + { + data=str; + added_data(0,length); + }else + { + data=0; + } + + FCPools::get()->add_pool(furl, this); + + wake_up_all_readers(); + + // Call every trigger callback + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + call_callback(t->callback, t->cl_data); + } + triggers_list.empty(); + } +} + +int +DataPool::get_length(void) const +{ + // Not connected and length has been guessed + // Or connected to a file + // Or connected to a pool, but length was preset + int retval=(-1); + if (length>=0) + { + retval=length; + }else if (pool) + { + int plength=pool->get_length(); + if (plength>=0) + retval=plength-start; + } + return retval; +} + +int +DataPool::get_size(int dstart, int dlength) const +{ + if (dlength<0 && length>0) + { + dlength=length-dstart; + if (dlength<0) return 0; + } + + if (pool) return pool->get_size(start+dstart, dlength); + else if (furl.is_local_file_url()) + { + if (start+dstart+dlength>length) return length-(start+dstart); + else return dlength; + } else + { + if (dlength<0) + { + GCriticalSectionLock lock((GCriticalSection *) &data_lock); + dlength=data->size()-dstart; + } + return (dlength<0)?0:(block_list->get_bytes(dstart, dlength)); + } +} + +void +DataPool::add_data(const void * buffer, int size) + // This function adds data sequentially at 'add_at' position +{ + DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n"); + DEBUG_MAKE_INDENT(3); + + add_data(buffer, add_at, size); + add_at+=size; +} + +void +DataPool::add_data(const void * buffer, int offset, int size) +{ + DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" << + offset << "...\n"); + DEBUG_MAKE_INDENT(3); + + if (furl.is_local_file_url() || pool) + G_THROW( ERR_MSG("DataPool.add_data") ); + + // Add data to the data storage + { + GCriticalSectionLock lock(&data_lock); + if (offset>data->size()) + { + char ch=0; + data->seek(0, SEEK_END); + for(int i=data->size();i<offset;i++) + data->write(&ch, 1); + } else + { + data->seek(offset, SEEK_SET); + data->writall(buffer, size); + } + } + + added_data(offset, size); +} + +void +DataPool::added_data(const int offset, const int size) +{ + // Modify map of blocks + block_list->add_range(offset, size); + + // Wake up all threads, which may be waiting for this data + { + GCriticalSectionLock lock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + { + GP<Reader> reader=readers_list[pos]; + if (block_list->get_bytes(reader->offset, 1)) + { + DEBUG_MSG("waking up reader: offset=" << reader->offset << + ", size=" << reader->size << "\n"); + DEBUG_MAKE_INDENT(3); + reader->event.set(); + } + } + } + + // And call triggers + check_triggers(); + + // Do not undo the following two lines. The reason why we need them + // here is the connected DataPools, which use 'length' (more exactly + // has_data()) to see if they have all data required. So, right after + // all data has been added to the master DataPool, but before EOF + // is set, the master and slave DataPools disagree regarding if + // all data is there or not. These two lines solve the problem + GCriticalSectionLock lock(&data_lock); + if (length>=0 && data->size()>=length) + set_eof(); +} + +bool +DataPool::has_data(int dstart, int dlength) +{ + if (dlength<0 && length>0) + dlength=length-dstart; + return (pool?(pool->has_data(start+dstart, dlength)) + :((furl.is_local_file_url())?(start+dstart+dlength<=length) + :((dlength<0)?is_eof() + :(block_list->get_bytes(dstart, dlength)==dlength)))); +} + +int +DataPool::get_data(void * buffer, int offset, int sz) +{ + return get_data(buffer, offset, sz, 0); +} + +class DataPool::Incrementor +{ +private: + Counter & counter; +public: + Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();} + ~Incrementor() {counter.dec();} +}; + +int +DataPool::get_data(void * buffer, int offset, int sz, int level) +{ + DEBUG_MSG("DataPool::get_data()\n"); + DEBUG_MAKE_INDENT(3); + Incrementor inc(*active_readers); + + if (stop_flag) + G_THROW( DataPool::Stop ); + if (stop_blocked_flag && !is_eof() && + !has_data(offset, sz)) + G_THROW( DataPool::Stop ); + + if (sz < 0) + G_THROW( ERR_MSG("DataPool.bad_size") ); + + if (! sz) + return 0; + + if (pool) + { + DEBUG_MSG("DataPool::get_data(): from pool\n"); + DEBUG_MAKE_INDENT(3); + int retval=0; + if (length>0 && offset+sz>length) + sz=length-offset; + if (sz<0) + sz=0; + for(;;) + { + // Ask the underlying (master) DataPool for data. Note, that + // master DataPool may throw the "DATA_POOL_REENTER" exception + // demanding all readers to restart. This happens when + // a DataPool in the chain of DataPools stops. All readers + // should return to the most upper level and then reenter the + // DataPools hierarchy. Some of them will be stopped by + // DataPool::Stop exception. + G_TRY + { + if(stop_flag||stop_blocked_flag&&!is_eof()&&!has_data(offset, sz)) + G_THROW( DataPool::Stop ); + retval=pool->get_data(buffer, start+offset, sz, level+1); + } + G_CATCH(exc) + { + pool->clear_stream(true); + if ((exc.get_cause() != GUTF8String( ERR_MSG("DataPool.reenter") ) ) || level) + G_RETHROW; + } G_ENDCATCH; + pool->clear_stream(true); + return retval; + } + } + else if(data && data->is_static() && eof_flag) + { + DEBUG_MSG("DataPool::get_data(): static\n"); + DEBUG_MAKE_INDENT(3); + // We're not connected to anybody => handle the data + int size=block_list->get_range(offset, sz); + if (size>0) + { + // Hooray! Some data is there + GCriticalSectionLock lock(&data_lock); + data->seek(offset, SEEK_SET); + return data->readall(buffer, size); + } + return 0; + } + else if (furl.is_local_file_url()) + { + DEBUG_MSG("DataPool::get_data(): from file\n"); + DEBUG_MAKE_INDENT(3); + if (length>0 && offset+sz>length) + sz=length-offset; + if (sz<0) + sz=0; + + GP<OpenFiles_File> f=fstream; + if (!f) + { + GCriticalSectionLock lock(&class_stream_lock); + f=fstream; + if(!f) + { + fstream=f=OpenFiles::get()->request_stream(furl, this); + } + } + GCriticalSectionLock lock2(&(f->stream_lock)); + f->stream->seek(start+offset, SEEK_SET); + return f->stream->readall(buffer, sz); + } + else + { + DEBUG_MSG("DataPool::get_data(): direct\n"); + DEBUG_MAKE_INDENT(3); + // We're not connected to anybody => handle the data + int size=block_list->get_range(offset, sz); + if (size>0) + { + // Hooray! Some data is there + GCriticalSectionLock lock(&data_lock); + data->seek(offset, SEEK_SET); + return data->readall(buffer, size); + } + + // No data available. + + // If there is no data and nothing else is expected, we can do + // two things: throw ByteStream::EndOfFile exception or return ZERO bytes. + // The exception is for the cases when the data flow has been + // terminated in the middle. ZERO bytes is for regular read() beyond + // the boundaries of legal data. The problem is to distinguish + // these two cases. We do it here with the help of analysis of the + // IFF structure of the data (which sets the 'length' variable). + // If we attempt to read beyond the [0, length[, ZERO bytes will be + // returned. Otherwise an ByteStream::EndOfFile exception will be thrown. + if (eof_flag) + { + if (length>0 && offset<length) + { + G_THROW( ByteStream::EndOfFile ); + } + else + { + return 0; + } + } + // Some data is still expected => add this reader to the + // list of readers and call virtual wait_for_data() + DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n"); + DEBUG_MSG("offset=" << offset << ", size=" << sz << + ", data_size=" << data->size() << "\n"); + GP<Reader> reader=new Reader(offset, sz); + G_TRY + { + { + GCriticalSectionLock slock(&readers_lock); + readers_list.append(reader); + } + wait_for_data(reader); + } + G_CATCH_ALL + { + { + GCriticalSectionLock slock(&readers_lock); + GPosition pos; + if (readers_list.search(reader, pos)) readers_list.del(pos); + } + G_RETHROW; + } + G_ENDCATCH; + + { + GCriticalSectionLock slock(&readers_lock); + GPosition pos; + if (readers_list.search(reader, pos)) readers_list.del(pos); + } + + // This call to get_data() should return immediately as there MUST + // be data in the buffer after wait_for_data(reader) returns + // or eof_flag should be TRUE + return get_data(buffer, reader->offset, reader->size, level); + } + return 0; +} + +void +DataPool::wait_for_data(const GP<Reader> & reader) + // This function may NOT return until there is some data for the + // given reader in the internal buffer +{ + DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset << + ", length=" << reader->size << "\n"); + DEBUG_MAKE_INDENT(3); + +#if THREADMODEL==NOTHREADS + G_THROW( ERR_MSG("DataPool.no_threadless") ); +#else + for(;;) + { + if (stop_flag) + G_THROW( DataPool::Stop ); + if (reader->reenter_flag) + G_THROW( ERR_MSG("DataPool.reenter") ); + if (eof_flag || block_list->get_bytes(reader->offset, 1)) + return; + if (pool || furl.is_local_file_url()) + return; + + if (stop_blocked_flag) + G_THROW( DataPool::Stop ); + + DEBUG_MSG("calling event.wait()...\n"); + reader->event.wait(); + } +#endif + + DEBUG_MSG("Got some data to read\n"); +} + +void +DataPool::wake_up_all_readers(void) +{ + DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + readers_list[pos]->event.set(); +} + +void +DataPool::set_eof(void) + // Has no effect on connected DataPools +{ + if (!furl.is_local_file_url() && !pool) + { + eof_flag=true; + + // Can we set the length now? + if (length<0) + { + GCriticalSectionLock lock(&data_lock); + length=data->size(); + } + + // Wake up all readers to let them rescan the flags + wake_up_all_readers(); + + // Activate all trigger callbacks with negative threshold + check_triggers(); + } +} + +void +DataPool::stop(bool only_blocked) +{ + DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked=" + << only_blocked << "\n"); + DEBUG_MAKE_INDENT(3); + + if (only_blocked) stop_blocked_flag=true; + else stop_flag=true; + + + wake_up_all_readers(); + + // Now let all readers, which already go thru to the master DataPool, + // come back and reenter. While reentering some of them will go + // thru this DataPool again and will be stopped (DataPool::Stop exception) + // Others (which entered the master DataPool thru other slave DataPools) + // will simply continue waiting for their data. + if (pool) + { + // This loop is necessary because there may be another thread, which + // is going down thru the DataPool chain and did not reach the + // lowest "master" DataPool yet. Since it didn't reach it yet, + // the "pool->restart_readers()" will not restart it. So we're going + // to continue issuing this command until we get rid of all + // "active_readers" + while(*active_readers) + { +#if (THREADMODEL==COTHREADS) || (THREADMODEL==MACTHREADS) + GThread::yield(); +#endif + pool->restart_readers(); + } + } +} + +void +DataPool::restart_readers(void) +{ + DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock slock(&readers_lock); + for(GPosition pos=readers_list;pos;++pos) + { + GP<Reader> reader=readers_list[pos]; + reader->reenter_flag=true; + reader->event.set(); + } + + if (pool) + pool->restart_readers(); +} + +void +DataPool::load_file(void) +{ + DEBUG_MSG("DataPool::load_file() called\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + { + DEBUG_MSG("passing the request down.\n"); + pool->load_file(); + } else if (furl.is_local_file_url()) + { + DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n"); + + GCriticalSectionLock lock1(&class_stream_lock); + GP<OpenFiles_File> f=fstream; + if (!f) + { + fstream=f=OpenFiles::get()->request_stream(furl, this); + } + { // Scope to de-allocate lock2 before stream gets released + GCriticalSectionLock lock2(&(f->stream_lock)); + + data=ByteStream::create(); + block_list->clear(); + FCPools::get()->del_pool(furl, this); + furl=GURL(); + + const GP<ByteStream> gbs=f->stream; + gbs->seek(0, SEEK_SET); + data=gbs->duplicate(); + added_data(0,data->size()); + set_eof(); +// char buffer[1024]; +// int length; +// while((length=f->stream->read(buffer, 1024))) +// add_data(buffer, length); + // No need to set EOF. It should already be set. + OpenFiles::get()->stream_released(f->stream, this); + } + fstream=0; + } else { DEBUG_MSG("Not connected\n"); } +} + +void +DataPool::load_file(const GURL &url ) +{ + FCPools::get()->load_file(url); +} + +void +DataPool::check_triggers(void) + // This function is for not connected DataPools only +{ + DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n"); + DEBUG_MAKE_INDENT(3); + + if (!pool && !furl.is_local_file_url()) + while(true) + { + GP<Trigger> trigger; + + // First find a candidate (trigger, which needs to be called) + // Don't remove it from the list yet. del_trigger() should + // be able to find it if necessary and disable. + { + GCriticalSectionLock list_lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + { + GP<Trigger> t=triggers_list[pos]; + if (is_eof() || t->length>=0 && + block_list->get_bytes(t->start, t->length)==t->length) + { + trigger=t; + break; + } + } + } + + if (trigger) + { + // Now check that the trigger is not disabled + // and lock the trigger->disabled lock for the duration + // of the trigger. This will block the del_trigger() and + // will postpone client's destruction (usually following + // the call to del_trigger()) + { + GMonitorLock lock(&trigger->disabled); + if (!trigger->disabled) + call_callback(trigger->callback, trigger->cl_data); + } + + // Finally - remove the trigger from the list. + GCriticalSectionLock list_lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;++pos) + if (triggers_list[pos]==trigger) + { + triggers_list.del(pos); + break; + } + } else break; + } +} + +void +// DataPool::add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data) +{ + if (thresh>=0) + add_trigger(0, thresh+1, callback, cl_data); + else + add_trigger(0, -1, callback, cl_data); +} + +void +DataPool::add_trigger(int tstart, int tlength, +// void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) + void (* callback)(void *), void * cl_data) +{ + DEBUG_MSG("DataPool::add_trigger(): start=" << tstart << + ", length=" << tlength << ", func=" << (void *) callback << "\n"); + DEBUG_MAKE_INDENT(3); + + if (callback) + { + if (is_eof()) + { + call_callback(callback, cl_data); + }else + { + if (pool) + { + // We're connected to a DataPool + // Just pass the triggers down remembering it in the list + if (tlength<0 && length>0) tlength=length-tstart; + GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data); + pool->add_trigger(start+tstart, tlength, callback, cl_data); + GCriticalSectionLock lock(&triggers_lock); + triggers_list.append(trigger); + } else if (!furl.is_local_file_url()) + { + // We're not connected to anything and maintain our own data + if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength) + call_callback(callback, cl_data); + else + { + GCriticalSectionLock lock(&triggers_lock); + triggers_list.append(new Trigger(tstart, tlength, callback, cl_data)); + } + } + } + } +} + +void +// DataPool::del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data) +DataPool::del_trigger(void (* callback)(void *), void * cl_data) +{ + DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n"); + DEBUG_MAKE_INDENT(3); + + for(;;) + { + GP<Trigger> trigger; + { + GCriticalSectionLock lock(&triggers_lock); + for(GPosition pos=triggers_list;pos;) + { + GP<Trigger> t=triggers_list[pos]; + if (t->callback==callback && t->cl_data==cl_data) + { + trigger=t; + GPosition this_pos=pos; + ++pos; + triggers_list.del(this_pos); + break; + } else + ++pos; + } + } + + // Above we removed the trigger from the list and unlocked the list + // Now we will disable it and will wait if necessary (if the + // trigger is currently being processed by check_triggers()) + // check_triggers() locks the trigger for the duration of the + // trigger callback. Thus we will wait for the trigger callback + // to finish and avoid client's destruction. + if (trigger) + trigger->disabled=1; + else + break; + } + + if (pool) + pool->del_trigger(callback, cl_data); +} + +void +// DataPool::static_trigger_cb(GP<GPEnabled> &cl_data) +DataPool::static_trigger_cb(void *cl_data) +{ +// GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data; + GP<DataPool> d=(DataPool *)cl_data; + d->trigger_cb(); +} + +void +DataPool::trigger_cb(void) + // This function may be triggered by the DataPool, which we're + // connected to, or by ourselves, if we're connected to nothing +{ + // Don't want to be destroyed while I'm here. Can't use GP<> life saver + // because it may be called from the constructor + GCriticalSectionLock lock(&trigger_lock); + + DEBUG_MSG("DataPool::trigger_cb() called\n"); + DEBUG_MAKE_INDENT(3); + + if (pool) + { + // Connected to a pool + // We may be here when either EOF is set on the master DataPool + // Or when it may have learnt its length (from IFF or whatever) + if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true; + } else if (!furl.is_local_file_url()) + { + // Not connected to anything => Try to guess the length + if (length<0) analyze_iff(); + + // Failed to analyze? Check, maybe it's EOF already + if (length<0 && is_eof()) + { + GCriticalSectionLock lock(&data_lock); + length=data->size(); + } + } +} + +void +DataPool::analyze_iff(void) + // In order to display decode progress properly, we need to know + // the size of the data. It's trivial to figure it out if is_eof() + // is true. Otherwise we need to make a prediction. Luckily all + // DjVuFiles have IFF structure, which makes it possible to do it. + // If due to some reason we fail, the length will remain -1. +{ + DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n"); + DEBUG_MSG("in order to predict the DataPool's size\n"); + DEBUG_MAKE_INDENT(3); + + GP<ByteStream> str=get_stream(); + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int size; + if ((size=iff.get_chunk(chkid)) && size>=0) + { + length=size+iff.tell()-4; + DEBUG_MSG("Got size=" << size << ", length=" << length << "\n"); + } +} + + +//**************************************************************************** +//****************************** PoolByteStream ****************************** +//**************************************************************************** + +// This is an internal ByteStream receiving data from the associated DataPool. +// It's just a sequential interface, nothing more. All the job for data +// retrieval, waiting and thread synchronization is done by DataPool + +class PoolByteStream : public ByteStream +{ +public: + PoolByteStream(GP<DataPool> data_pool); + virtual ~PoolByteStream() {}; + + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual long tell(void) const ; + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); +private: + // Don't make data_pool GP<>. The problem is that DataPool creates + // and soon destroys this ByteStream from the constructor. Since + // there are no other pointers to the DataPool created yet, it becomes + // destroyed immediately :( + DataPool * data_pool; + GP<DataPool> data_pool_lock; + long position; + + char buffer[512]; + size_t buffer_size; + size_t buffer_pos; + + // Cancel C++ default stuff + PoolByteStream & operator=(const PoolByteStream &); +}; + +inline +PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) : + data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0) +{ + if (!data_pool) + G_THROW( ERR_MSG("DataPool.zero_DataPool") ); + + // Secure the DataPool if possible. If we're called from DataPool + // constructor (get_count()==0) there is no need to secure at all. + if (data_pool->get_count()) data_pool_lock=data_pool; +} + +size_t +PoolByteStream::read(void *data, size_t size) +{ + if (buffer_pos >= buffer_size) { + if (size >= sizeof(buffer)) { + // Direct read + size = data_pool->get_data(data, position, size); + position += size; + return size; + } else { + // Refill buffer + buffer_size = data_pool->get_data(buffer, position, sizeof(buffer)); + buffer_pos=0; + } + } + if (buffer_pos + size >= buffer_size) + size = buffer_size - buffer_pos; + memcpy(data, buffer+buffer_pos, size); + buffer_pos += size; + position += size; + return size; +} + +size_t +PoolByteStream::write(const void *buffer, size_t size) +{ + G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()"); // PoolByteStream::write() is not implemented. + return 0; // For compiler not to bark +} + +long +PoolByteStream::tell(void) const +{ + return position; +} + +int +PoolByteStream::seek(long offset, int whence, bool nothrow) +{ + int retval=(-1); + switch(whence) + { + case SEEK_CUR: + offset+=position; + // fallthrough; + case SEEK_SET: + if(offset<position) + { + if((int)(offset+buffer_pos)>=(int)position) + { + buffer_pos-=position-offset; + }else + { + buffer_size=0; + } + position=offset; + }else if(offset>position) + { + buffer_pos+=(offset-position)-1; + position=offset-1; + unsigned char c; + if(read(&c,1)<1) + { + G_THROW( ByteStream::EndOfFile ); + } + } + retval=0; + break; + case SEEK_END: + if(! nothrow) + G_THROW( ERR_MSG("DataPool.seek_backward") ); + break; + } + return retval; +} + +void +DataPool::close_all(void) +{ + OpenFiles::get()->close_all(); +} + + +GP<ByteStream> +DataPool::get_stream(void) +{ + if(data && data->is_static()) + { + GCriticalSectionLock lock(&data_lock); + data->seek(0, SEEK_SET); + return data->duplicate(length); + }else + { + return new PoolByteStream(this); + } +} + + +inline +DataPool::Counter::operator int(void) const +{ + GCriticalSectionLock lk((GCriticalSection *) &lock); + int cnt=counter; + return cnt; +} + +inline void +DataPool::Counter::inc(void) +{ + GCriticalSectionLock lk(&lock); + counter++; +} + +inline void +DataPool::Counter::dec(void) +{ + GCriticalSectionLock lk(&lock); + counter--; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DataPool.h b/kviewshell/plugins/djvu/libdjvu/DataPool.h new file mode 100644 index 00000000..fb4bea4e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DataPool.h @@ -0,0 +1,627 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DATAPOOL_H +#define _DATAPOOL_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GThreads.h" +#include "GString.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DataPool.h + Files #"DataPool.h"# and #"DataPool.cpp"# implement classes \Ref{DataPool} + and \Ref{DataRange} used by DjVu decoder to access data. + + The main goal of class \Ref{DataPool} is to provide concurrent access + to the same data from many threads with a possibility to add data + from yet another thread. It is especially important in the case of the + Netscape plugin when data is not immediately available, but decoding + should be started as soon as possible. In this situation it is vital + to provide transparent access to the data from many threads possibly + blocking readers that try to access information that has not been + received yet. + + When the data is local though, it can be accessed directly using + standard IO mechanism. To provide a uniform interface for decoding + routines, \Ref{DataPool} supports file mode as well. + + @memo Thread safe data storage + @author Andrei Erofeev <[email protected]> + @version #$Id: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** Thread safe data storage. + The purpose of #DataPool# is to provide a uniform interface for + accessing data from decoding routines running in a multi-threaded + environment. Depending on the mode of operation it may contain the + actual data, may be connected to another #DataPool# or may be mapped + to a file. Regardless of the mode, the class returns data in a + thread-safe way, blocking reading threads if there is no data of + interest available. This blocking is especially useful in the + networking environment (plugin) when there is a running decoding thread, + which wants to start decoding as soon as there is just one byte available + blocking if necessary. + + Access to data in a #DataPool# may be direct (Using \Ref{get_data}() + function) or sequential (See \Ref{get_stream}() function). + + If the #DataPool# is not connected to anything, that is it contains + some real data, this data can be added to it by means of two + \Ref{add_data}() functions. One of them adds data sequentially maintaining + the offset of the last block of data added by it. The other can store + data anywhere. Thus it's important to realize, that there may be "white + spots" in the data storage. + + There is also a way to test if data is available for some given data + range (See \Ref{has_data}()). In addition to this mechanism, there are + so-called {\em trigger callbacks}, which are called, when there is + all data available for a given data range. + + Let us consider all modes of operation in details: + + \begin{enumerate} + \item {\bf Not connected #DataPool#}. In this mode the #DataPool# + contains some real data. As mentioned above, it may be added + by means of two functions \Ref{add_data}() operating independent + of each other and allowing to add data sequentially and + directly to any place of data storage. It's important to call + function \Ref{set_eof}() after all data has been added. + + Functions like \Ref{get_data}() or \Ref{get_stream}() can + be used to obtain direct or sequential access to the data. As + long as \Ref{is_eof}() is #FALSE#, #DataPool# will block every + reader, which is trying to read unavailable data until it + really becomes available. But as soon as \Ref{is_eof}() is + #TRUE#, any attempt to read non-existing data will read #0# bytes. + + Taking into account the fact, that #DataPool# was designed to + store DjVu files, which are in IFF formats, it becomes possible + to predict the size of the #DataPool# as soon as the first + #32# bytes have been added. This is invaluable for estimating + download progress. See function \Ref{get_length}() for details. + If this estimate fails (which means, that stored data is not + in IFF format), \Ref{get_length}() returns #-1#. + + Triggers may be added and removed by means of \Ref{add_trigger}() + and \Ref{del_trigger}() functions. \Ref{add_trigger}() takes + a data range. As soon as all data in that data range is + available, the trigger callback will be called. + + All trigger callbacks will be called when #EOF# condition + has been set. + + \item {\bf #DataPool# connected to another #DataPool#}. In this + {\em slave} mode you can map a given #DataPool# to any offsets + range inside another #DataPool#. You can connect the slave + #DataPool# even if there is no data in the master #DataPool#. + Any \Ref{get_data}() request will be forwarded to the master + #DataPool#, and it will be responsible for blocking readers + trying to access unavailable data. + + The usage of \Ref{add_data}() functions is prohibited for + connected #DataPool#s. + + The offsets range used to map a slave #DataPool# can be fully + specified (both start offset and length are positive numbers) + or partially specified (the length is negative). In this mode + the slave #DataPool# is assumed to extend up to the end + of the master #DataPool#. + + Triggers may be used with slave #DataPool#s as well as with + the master ones. + + Calling \Ref{stop}() function of a slave will stop only the slave + (and any other slave connected to it), but not the master. + + \Ref{set_eof}() function is meaningless for slaves. They obtain + the #ByteStream::EndOfFile# status from their master. + + Depending on the offsets range passed to the constructor, + \Ref{get_length}() returns different values. If the length + passed to the constructor was positive, then it is returned + by \Ref{get_length}() all the time. Otherwise the value returned + is either #-1# if master's length is still unknown (it didn't + manage to parse IFF data yet) or it is calculated as + #masters_length-slave_start#. + + \item {\bf #DataPool# connected to a file}. This mode is quite similar + to the case, when the #DataPool# is connected to another + #DataPool#. Similarly, the #DataPool# stores no data inside. + It just forwards all \Ref{get_data}() requests to the underlying + source (a file in this case). Thus these requests will never + block the reader. But they may return #0# if there is no data + available at the requested offset. + + The usage of \Ref{add_data}() functions is meaningless and + is prohibited. + + \Ref{is_eof}() function always returns #TRUE#. Thus \Ref{set_eof}() + us meaningless and does nothing. + + \Ref{get_length}() function always returns the file size. + + Calling \Ref{stop}() function will stop this #DataPool# and + any other slave connected to it. + + Trigger callbacks passed through \Ref{add_trigger}() function + are called immediately. + + This mode is useful to read and decode DjVu files without reading + and storing them in full in memory. + \end{enumerate} +*/ + +class DataPool : public GPEnabled +{ +public: // Classes used internally by DataPool + // These are declared public to support buggy C++ compilers. + class Incrementor; + class Reader; + class Trigger; + class OpenFiles; + class OpenFiles_File; + class BlockList; + class Counter; +protected: + DataPool(void); + +public: + /** @name Initialization */ + //@{ + /** Default creator. Will prepare #DataPool# for accepting data + added through functions \Ref{add_data}(). Use \Ref{connect}() + functions if you want to map this #DataPool# to another or + to a file. */ + static GP<DataPool> create(void); + + /** Creates and initialized the #DataPool# with data from stream #str#. + The constructor will read the stream's contents and add them + to the pool using the \Ref{add_data}() function. Afterwards it + will call \Ref{set_eof}() function, and no other data will be + allowed to be added to the pool. */ + static GP<DataPool> create(const GP<ByteStream> & str); + + /** Initializes the #DataPool# in slave mode and connects it + to the specified offsets range of the specified master #DataPool#. + It is equivalent to calling default constructor and function + \Ref{connect}(). + + @param master_pool Master #DataPool# providing data for this slave + @param start Beginning of the offsets range which the slave is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the master #DataPool#. + */ + static GP<DataPool> create(const GP<DataPool> & master_pool, int start=0, int length=-1); + + /** Initializes the #DataPool# in slave mode and connects it + to the specified offsets range of the specified file. + It is equivalent to calling default constructor and function + \Ref{connect}(). + @param url Name of the file to connect to. + @param start Beginning of the offsets range which the #DataPool# is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the file. + */ + static GP<DataPool> create(const GURL &url, int start=0, int length=-1); + + virtual ~DataPool(); + + /** Switches the #DataPool# to slave mode and connects it to the + specified offsets range of the master #DataPool#. + @param master_pool Master #DataPool# providing data for this slave + @param start Beginning of the offsets range which the slave is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the master #DataPool#. + */ + void connect(const GP<DataPool> & master_pool, int start=0, int length=-1); + /** Connects the #DataPool# to the specified offsets range of + the named #url#. + @param url Name of the file to connect to. + @param start Beginning of the offsets range which the #DataPool# is + mapped into + @param length Length of the offsets range. If negative, the range + is assumed to extend up to the end of the file. + */ + void connect(const GURL &url, int start=0, int length=-1); + //@} + + /** Tells the #DataPool# to stop serving readers. + + If #only_blocked# flag is #TRUE# then only those requests will + be processed, which would not block. Any attempt to get non-existing + data would result in a #STOP# exception (instead of blocking until + data is available). + + If #only_blocked# flag is #FALSE# then any further attempt to read + from this #DataPool# (as well as from any #DataPool# connected + to this one) will result in a #STOP# exception. */ + void stop(bool only_blocked=false); + + /** @name Adding data. + Please note, that these functions are for not connected #DataPool#s + only. You can not add data to a #DataPool#, which is connected + to another #DataPool# or to a file. + */ + //@{ + /** Appends the new block of data to the #DataPool#. There are two + \Ref{add_data}() functions available. One is for adding data + sequentially. It keeps track of the last byte position, which has + been stored {\bf by it} and always appends the next block after + this position. The other \Ref{add_data}() can store data anywhere. + + The function will unblock readers waiting for data if this data + arrives with this block. It may also trigger some {\em trigger + callbacks}, which may have been added by means of \Ref{add_trigger}() + function. + + {\bf Note:} After all the data has been added, it's necessary + to call \Ref{set_eof}() to tell the #DataPool# that nothing else + is expected. + + {\bf Note:} This function may not be called if the #DataPool# + has been connected to something. + + @param buffer data to append + @param size length of the {\em buffer} + */ + void add_data(const void * buffer, int size); + + /** Stores the specified block of data at the specified offset. + Like the function above this one can also unblock readers + waiting for data and engage trigger callbacks. The difference + is that {\bf this} function can store data anywhere. + + {\bf Note:} After all the data has been added, it's necessary + to call \Ref{set_eof}() to tell the #DataPool# that nothing else + is expected. + + {\bf Note:} This function may not be called if the #DataPool# + has been connected to something. + + @param buffer data to store + @param offset where to store the data + @param size length of the {\em buffer} */ + void add_data(const void * buffer, int offset, int size); + + /** Tells the #DataPool# that all data has been added and nothing else + is anticipated. When #EOF# is true, any reader attempting to read + non existing data will not be blocked. It will either read #ZERO# + bytes or will get an #ByteStream::EndOfFile# exception (see \Ref{get_data}()). + Calling this function will also activate all registered trigger + callbacks. + + {\bf Note:} This function is meaningless and does nothing + when the #DataPool# is connected to another #DataPool# or to + a file. */ + void set_eof(void); + //@} + + /** @name Accessing data. + These functions provide direct and sequential access to the + data of the #DataPool#. If the #DataPool# is not connected + (contains some real data) then it handles the requests itself. + Otherwise they are forwarded to the master #DataPool# or the file. + */ + //@{ + /** Attempts to return a block of data at the given #offset# + of the given #size#. + + \begin{enumerate} + \item If the #DataPool# is connected to another #DataPool# or + to a file, the request will just be forwarded to them. + \item If the #DataPool# is not connected to anything and + some of the data requested is in the internal buffer, + the function copies available data to #buffer# and returns + immediately. + + If there is no data available, and \Ref{is_eof}() returns + #FALSE#, the reader (and the thread) will be {\bf blocked} + until the data actually arrives. Please note, that since + the reader is blocked, it should run in a separate thread + so that other threads have a chance to call \Ref{add_data}(). + If there is no data available, but \Ref{is_eof}() is #TRUE# + the behavior is different and depends on the #DataPool#'s + estimate of the file size: + \begin{itemize} + \item If #DataPool# learns from the IFF structure of the + data, that its size should be greater than it + really is, then any attempt to read non-existing + data in the range of {\em valid} offsets will + result in an #ByteStream::EndOfFile# exception. This is done to + indicate, that there was an error in adding data, + and the data requested is {\bf supposed} to be + there, but has actually not been added. + \item If #DataPool#'s expectations about the data size + coincide with the reality then any attempt to + read data beyond the legal range of offsets will + result in #ZERO# bytes returned. + \end{itemize}. + \end{enumerate}. + + @param buffer Buffer to be filled with data + @param offset Offset in the #DataPool# to read data at + @param size Size of the {\em buffer} + @return The number of bytes actually read + @exception STOP The stream has been stopped + @exception EOF The requested data is not there and will not be added, + although it should have been. + */ + int get_data(void * buffer, int offset, int size); + + /** Returns a \Ref{ByteStream} to access contents of the #DataPool# + sequentially. By reading from the returned stream you basically + call \Ref{get_data}() function. Thus, everything said for it + remains true for the stream too. */ + GP<ByteStream> get_stream(void); + //@} + + /** @name State querying functions. */ + //@{ + /** Returns #TRUE# if this #DataPool# is connected to another #DataPool# + or to a file. */ + bool is_connected(void) const; + + /** Returns #TRUE# if all data available for offsets from + #start# till #start+length-1#. If #length# is negative, the + range is assumed to extend up to the end of the #DataPool#. + This function works both for connected and not connected #DataPool#s. + Once it returned #TRUE# for some offsets range, you can be + sure that the subsequent \Ref{get_data}() request will not block. + */ + bool has_data(int start, int length); + + /* Returns #TRUE# if no more data is planned to be added. + + {\bf Note:} This function always returns #TRUE# when the #DataPool# + has been initialized with a file name. */ + bool is_eof(void) const {return eof_flag;} + + /** Returns the {\em length} of data in the #DataPool#. The value + returned depends on the mode of operation: + \begin{itemize} + \item If the #DataPool# is not connected to anything then + the length returned is either calculated by interpreting + the IFF structure of stored data (if successful) or + by calculating the real size of data after \Ref{set_eof}() + has been called. Otherwise it is #-1#. + \item If the #DataPool# is connected to a file, the length + is calculated basing on the length passed to the + \Ref{connect}() function and the file size. + \item If the #DataPool# is connected to a master #DataPool#, + the length is calculated basing on the value returned + by the master's #get_length()# function and the length + passed to the \Ref{connect}() function. + \end{itemize}. */ + int get_length(void) const; + /** Returns the number of bytes of data available in this #DataPool#. + Contrary to the \Ref{get_length}() function, this one doesn't try + to interpret the IFF structure and predict the file length. + It just returns the number of bytes of data really available inside + the #DataPool#, if it contains data, or inside its range, if it's + connected to another #DataPool# or a file. */ + int get_size(void) const {return get_size(0, -1);} + //@} + + /** @name Trigger callbacks. + {\em Trigger callbacks} are special callbacks called when + all data for the given range of offsets has been made available. + Since reading unavailable data may result in a thread block, + which may be bad, the usage of {\em trigger callbacks} appears + to be a convenient way to signal availability of data. + + You can add a trigger callback in two ways: + \begin{enumerate} + \item By specifying a range. This is the most general case + \item By providing just one {\em threshold}. In this case + the range is assumed to start from offset #ZERO# and + last for {\em threshold}+1 bytes. + \end{enumerate} + */ + //@{ + /** Associates the specified {\em trigger callback} with the + given data range. + + {\bf Note:} The callback may be called immediately if all + data for the given range is already available or #EOF# is #TRUE#. + + @param start The beginning of the range for which all data + should be available + @param length If the {\em length} is not negative then the callback + will be called when there is data available for every + offset from {\em start} to {\em start+length-1}. + If {\em thresh} is negative, the callback is called after + #EOF# condition has been set. + @param callback Function to call + @param cl_data Argument to pass to the callback when it's called. */ + void add_trigger(int start, int length, +// void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + void (* callback)(void *), void * cl_data); + + /** Associates the specified {\em trigger callback} with the + specified threshold. + + This function is a simplified version of the function above. + The callback will be called when there is data available for + every offset from #0# to #thresh#, if #thresh# is positive, or + when #EOF# condition has been set otherwise. */ +// void add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + void add_trigger(int thresh, void (* callback)(void *), void * cl_data); + + /** Use this function to unregister callbacks, which are no longer + needed. {\bf Note!} It's important to do it when the client + is about to be destroyed. */ + void del_trigger(void (* callback)(void *), void * cl_data); +// void del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data); + //@} + + /** Loads data from the file into memory. This function is only useful + for #DataPool#s getting data from a file. It descends the #DataPool#s + hierarchy until it either reaches a file-connected #DataPool# + or #DataPool# containing the real data. In the latter case it + does nothing, in the first case it makes the #DataPool# read all + data from the file into memory and stop using the file. + + This may be useful when you want to overwrite the file and leave + existing #DataPool#s with valid data. */ + void load_file(void); + /** This function will make every #DataPool# in the program, which + is connected to a file, to load the file contents to the main + memory and close the file. This feature is important when you + want to do something with the file like remove or overwrite it + not affecting the rest of the program. */ + static void load_file(const GURL &url); + + /** This function will remove OpenFiles filelist. */ + static void close_all(void); + + // Internal. Used by 'OpenFiles' + void clear_stream(const bool release = true); + + /** Useful in comparing data pools. Returns true if dirived from + same URL or bytestream. */ + bool simple_compare(DataPool &pool) const; +private: + bool eof_flag; + bool stop_flag; + bool stop_blocked_flag; + + Counter *active_readers; + + // Source or storage of data + GP<DataPool> pool; + GURL furl; + GP<OpenFiles_File> fstream; + GCriticalSection class_stream_lock; + GP<ByteStream> data; + GCriticalSection data_lock; + BlockList *block_list; + int add_at; + int start, length; + + // List of readers waiting for data + GPList<Reader> readers_list; + GCriticalSection readers_lock; + + // Triggers + GPList<Trigger> triggers_list; // List of passed or our triggers + GCriticalSection triggers_lock; // Lock for the list above + GCriticalSection trigger_lock; // Lock for static_trigger_cb() + + void init(void); + void wait_for_data(const GP<Reader> & reader); + void wake_up_all_readers(void); + void check_triggers(void); + int get_data(void * buffer, int offset, int size, int level); + int get_size(int start, int length) const; + void restart_readers(void); + +// static void static_trigger_cb(GP<GPEnabled> &); + static void static_trigger_cb(void *); + void trigger_cb(void); + void analyze_iff(void); + void added_data(const int offset, const int size); +public: + static const char *Stop; + friend class FCPools; +}; + +inline bool +DataPool::simple_compare(DataPool &pool) const +{ + // return true if these pools are identical. False means they may or may + // not be identical. + return (this == &pool) + ||(furl.is_valid()&&!furl.is_empty()&&pool.furl.is_valid()&&(furl == pool.furl)) + ||(data && (data == pool.data)); +} + +inline bool +DataPool::is_connected(void) const +{ + return furl.is_local_file_url() || pool!=0; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp new file mode 100644 index 00000000..83f9df78 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp @@ -0,0 +1,839 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir.cpp,v 1.10 2004/05/05 15:12:42 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDir.h" +#include "BSByteStream.h" +#include "GURL.h" +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GP<DjVmDir::File> +DjVmDir::File::create(const GUTF8String &load_name, + const GUTF8String &save_name, const GUTF8String &title, + const FILE_TYPE file_type) +{ + File *file_ptr=new File(); + GP<File> file=file_ptr; + file_ptr->set_load_name(load_name); + file_ptr->set_save_name(save_name); + file_ptr->set_title(title); + file_ptr->flags=(file_type & TYPE_MASK); + return file; +} + +const GUTF8String & +DjVmDir::File::check_save_name(const bool xis_bundled) +{ + if(!xis_bundled && !valid_name) + { + GUTF8String retval=name.length()?name:id; + if(GUTF8String(GNativeString(retval)) != retval) + { + const_cast<bool &>(valid_name)=true; + char *buf; + GPBuffer<char> gbuf(buf,2*retval.length()+1); + char *s=buf; + int i=0; + for(char c=retval[i++];c;) + { + static const char hex[]="0123456789ABCDEF"; + int len=retval.nextChar(i)-i; + if(len>1 || ((len == 1)&&(c&0x80))) + { + do + { + s++[0]=hex[(c>>4)&0xf]; + s++[0]=hex[(c&0xf)]; + c=retval[i++]; + } while(c && ((--len) > 0)); + }else + { + s++[0]=c; + c=retval[i++]; + } + } + s++[0]=0; + oldname=retval; + name=buf; + } + const_cast<bool &>(valid_name)=true; + } + return *(name.length()?&name:&id); +} + +const GUTF8String & +DjVmDir::File::get_save_name(void) const +{ + return *(name.length()?&name:&id); +} + +void +DjVmDir::File::set_load_name(const GUTF8String &xid) +{ + GURL url=GURL::UTF8(xid); + if(!url.is_valid()) + { + url=GURL::Filename::UTF8(xid); + } + id=url.fname(); +} + +void +DjVmDir::File::set_save_name(const GUTF8String &xname) +{ + GURL url; + valid_name=false; + if(!xname.length()) + { + GURL url=GURL::UTF8(id); + if(!url.is_valid()) + { + name=id; + }else + { + name=url.fname(); + } + }else + { + GURL url=GURL::UTF8(xname); + if(!url.is_valid()) + { + url=GURL::Filename::UTF8(xname); + } + name=url.fname(); + } + oldname=""; +} + +/* DjVmDir::File */ + +DjVmDir::File::File(void) : offset(0), size(0), valid_name(false), + flags(0), page_num(-1) { } + +GUTF8String +DjVmDir::File::get_str_type(void) const +{ + GUTF8String type; + switch(flags & TYPE_MASK) + { + case INCLUDE: + type="INCLUDE"; + break; + case PAGE: + type="PAGE"; + break; + case THUMBNAILS: + type="THUMBNAILS"; + break; + case SHARED_ANNO: + type="SHARED_ANNO"; + break; + default: + // Internal error: please modify DjVmDir::File::get_str_type() + // to contain all possible File types. + G_THROW( ERR_MSG("DjVmDir.get_str_type") ); + } + return type; +} + + +const int DjVmDir::version=1; + +void +DjVmDir::decode(const GP<ByteStream> &gstr) +{ + ByteStream &str=*gstr; + DEBUG_MSG("DjVmDir::decode(): decoding contents of 'DIRM' chunk...\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + GPosition pos; + + files_list.empty(); + page2file.resize(-1); + name2file.empty(); + id2file.empty(); + title2file.empty(); + + int ver=str.read8(); + bool bundled=(ver & 0x80)!=0; + ver&=0x7f; + + DEBUG_MSG("DIRM version=" << ver << ", our version=" << version << "\n"); + if (ver>version) + G_THROW( ERR_MSG("DjVmDir.version_error") "\t" + + GUTF8String(version) + "\t" + GUTF8String(ver)); + // Unable to read DJVM directories of versions higher than xxx + // Data version number is yyy. + DEBUG_MSG("bundled directory=" << bundled << "\n"); + DEBUG_MSG("reading the directory records...\n"); + int files=str.read16(); + DEBUG_MSG("number of files=" << files << "\n"); + + if (files) + { + DEBUG_MSG("reading offsets (and sizes for ver==0)\n"); + for(int nfile=0;nfile<files;nfile++) + { + GP<File> file=new File(); + files_list.append(file); + if (bundled) + { + file->offset=str.read32(); + if (ver==0) + file->size=str.read24(); + if (file->offset==0) + G_THROW( ERR_MSG("DjVmDir.no_indirect") ); + } else + { + file->offset=file->size=0; + } + } + + GP<ByteStream> gbs_str=BSByteStream::create(gstr); + ByteStream &bs_str=*gbs_str; + if (ver>0) + { + DEBUG_MSG("reading and decompressing sizes...\n"); + for(GPosition pos=files_list;pos;++pos) + files_list[pos]->size=bs_str.read24(); + } + + DEBUG_MSG("reading and decompressing flags...\n"); + for(pos=files_list;pos;++pos) + files_list[pos]->flags=bs_str.read8(); + + if (!ver) + { + DEBUG_MSG("converting flags from version 0...\n"); + for(pos=files_list;pos;++pos) + { + unsigned char flags_0=files_list[pos]->flags; + unsigned char flags_1; + flags_1=(flags_0 & File::IS_PAGE_0)?(File::PAGE):(File::INCLUDE); + if (flags_0 & File::HAS_NAME_0) + flags_1|=File::HAS_NAME; + if (flags_0 & File::HAS_TITLE_0) + flags_1|=File::HAS_TITLE; + files_list[pos]->flags=flags_1; + } + } + + DEBUG_MSG("reading and decompressing names...\n"); + GTArray<char> strings; + char buffer[1024]; + int length; + while((length=bs_str.read(buffer, 1024))) + { + int strings_size=strings.size(); + strings.resize(strings_size+length-1); + memcpy((char*) strings+strings_size, buffer, length); + } + DEBUG_MSG("size of decompressed names block=" << strings.size() << "\n"); + + // Copy names into the files + const char * ptr=strings; + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + + file->id=ptr; + ptr+=file->id.length()+1; + if (file->flags & File::HAS_NAME) + { + file->name=ptr; + ptr+=file->name.length()+1; + } else + { + file->name=file->id; + } + if (file->flags & File::HAS_TITLE) + { + file->title=ptr; + ptr+=file->title.length()+1; + } else + file->title=file->id; + /* msr debug: multipage file, file->title is null. + DEBUG_MSG(file->name << ", " << file->id << ", " << file->title << ", " << + file->offset << ", " << file->size << ", " << + file->is_page() << "\n"); */ + } + + // Check that there is only one file with SHARED_ANNO flag on + int shared_anno_cnt=0; + for(pos=files_list;pos;++pos) + { + if (files_list[pos]->is_shared_anno()) + { + shared_anno_cnt++; + } + } + if (shared_anno_cnt>1) + G_THROW( ERR_MSG("DjVmDir.corrupt") ); + + // Now generate page=>file array for direct access + int pages=0; + for(pos=files_list;pos;++pos) + pages+=files_list[pos]->is_page() ? 1 : 0; + DEBUG_MSG("got " << pages << " pages\n"); + page2file.resize(pages-1); + int page_num=0; + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->is_page()) + { + page2file[page_num]=file; + file->page_num=page_num++; + } + } + + // Generate name2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (name2file.contains(file->name)) + G_THROW( ERR_MSG("DjVmDir.dupl_name") "\t" + file->name ); + name2file[file->name]=file; + } + + // Generate id2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (id2file.contains(file->id)) + G_THROW( ERR_MSG("DjVmDir.dupl_id") "\t" + file->id); + id2file[file->id]=file; + } + + // Generate title2file map + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->title.length()) + { + if (title2file.contains(file->title)) + G_THROW( ERR_MSG("DjVmDir.dupl_title") "\t" + file->title); + title2file[file->title]=file; + } + } + } +} + + +void +DjVmDir::encode(const GP<ByteStream> &gstr, const bool do_rename) const +{ + bool bundled = true; + GPosition pos = files_list; + if (files_list.size() && !files_list[pos]->offset) + bundled = false; + for (pos=files_list; pos; ++pos) + if ( !bundled != !files_list[pos]->offset) + // There directory contains both indirect and bundled records. + G_THROW( ERR_MSG("DjVmDir.bad_dir") ); + // Do the real work + encode(gstr, bundled, do_rename); +} + +void +DjVmDir::encode(const GP<ByteStream> &gstr, const bool bundled, const bool do_rename) const +{ + ByteStream &str=*gstr; + DEBUG_MSG("DjVmDir::encode(): encoding contents of the 'DIRM' chunk do_rename=" << do_rename << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + GPosition pos; + + DEBUG_MSG("encoding version number=" << version << ", bundled=" << bundled << "\n"); + str.write8(version | ((int) bundled<< 7)); + + DEBUG_MSG("storing the number of records=" << files_list.size() << "\n"); + str.write16(files_list.size()); + + if (files_list.size()) + { + // Check that there is only one file with shared annotations + int shared_anno_cnt=0; + for (pos=files_list; pos; ++pos) + if (files_list[pos]->is_shared_anno()) + shared_anno_cnt++; + if (shared_anno_cnt>1) + G_THROW( ERR_MSG("DjVmDir.multi_save") ); + + if (bundled) + { + // We need to store offsets uncompressed. That's because when + // we save a DjVmDoc, we first compress the DjVmDir with dummy + // offsets and after computing the real offsets we rewrite the + // DjVmDir, which should not change its size during this operation + DEBUG_MSG("storing offsets for every record\n"); + for (pos=files_list; pos; ++pos) + { + GP<File> file=files_list[pos]; + if (!file->offset) + // The directory contains record without offset + G_THROW( ERR_MSG("DjVmDir.bad_dir") ); + str.write32(file->offset); + } + } + + GP<ByteStream> gbs_str=BSByteStream::create(gstr, 50); + ByteStream &bs_str=*gbs_str; + DEBUG_MSG("storing and compressing sizes for every record\n"); + for (pos=files_list; pos; ++pos) + { + const GP<File> file(files_list[pos]); + bs_str.write24(file->size); + } + DEBUG_MSG("storing and compressing flags for every record\n"); + const bool xdo_rename=(do_rename||!bundled); + for(pos=files_list;pos;++pos) + { + const GP<File> file(files_list[pos]); + if(xdo_rename) + { + const GUTF8String new_id = file->name; + if (! new_id) + if(!file->oldname.length() || file->oldname == new_id) + file->flags &= ~File::HAS_NAME; + else + file->flags |= File::HAS_NAME; + } + else + { + if (!file->name.length() || file->name == file->id) + file->flags &= ~File::HAS_NAME; + else + file->flags |= File::HAS_NAME; + } + if (file->title.length() && (file->title!=file->id)) + file->flags |= File::HAS_TITLE; + else + file->flags &= ~File::HAS_TITLE; + + bs_str.write8(file->flags); + } + + DEBUG_MSG("storing and compressing names...\n"); + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + GUTF8String id; + GUTF8String name; + GUTF8String title; + if (xdo_rename) + { + id = file->name; + if (! id) + id = file->id; + if ((file->flags) & File::HAS_NAME) + name = file->oldname; + } + else + { + id=file->id; + if ((file->flags) & File::HAS_NAME) + name = file->name; + } + if ((file->flags) & File::HAS_TITLE) + title = file->title; + DEBUG_MSG("rename=" <<xdo_rename<<" id='" << id << "' name='" << name << "' title='" << title << "'\n"); + bs_str.writestring(id); + bs_str.write8(0); + if (name.length()) + { + bs_str.writestring(name); + bs_str.write8(0); + } + if (title.length()) + { + bs_str.writestring(title); + bs_str.write8(0); + } + } + } + DEBUG_MSG("done\n"); +} + +GP<DjVmDir::File> +DjVmDir::page_to_file(int page_num) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + return (page_num<page2file.size())?page2file[page_num]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::name_to_file(const GUTF8String & name) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (name2file.contains(name, pos))?name2file[pos]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::id_to_file(const GUTF8String &id) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (id2file.contains(id, pos))?id2file[pos]:(GP<DjVmDir::File>(0)); +} + +GP<DjVmDir::File> +DjVmDir::title_to_file(const GUTF8String &title) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + return (title2file.contains(title, pos))?title2file[pos]:(GP<DjVmDir::File>(0)); +} + +GPList<DjVmDir::File> +DjVmDir::get_files_list(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return files_list; +} + +int +DjVmDir::get_files_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return files_list.size(); +} + +int +DjVmDir::get_pages_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return page2file.size(); +} + +int +DjVmDir::get_file_pos(const File * f) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos&&(files_list[pos]!=f);++pos, cnt++) + continue; + return (pos)?cnt:(-1); +} + +int +DjVmDir::get_page_pos(int page_num) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GP<File> file=page_to_file(page_num); + return (file)?get_file_pos(file):(-1); +} + +GP<DjVmDir::File> +DjVmDir::get_shared_anno_file(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GP<File> file; + for(GPosition pos=files_list;pos;++pos) + { + GP<File> frec=files_list[pos]; + if (frec->is_shared_anno()) + { + file=frec; + break; + } + } + return file; +} + +int +DjVmDir::insert_file(const GP<File> & file, int pos_num) +{ + DEBUG_MSG("DjVmDir::insert_file(): name='" << file->name << "', pos=" << pos_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + if (pos_num<0) + pos_num=files_list.size(); + + // Modify maps +// if (! File::is_legal_id(file->id)) +// G_THROW( ERR_MSG("DjVmDir.bad_file") "\t" + file->id); + if (id2file.contains(file->id)) + G_THROW( ERR_MSG("DjVmDir.dupl_id2") "\t" + file->id); + if (name2file.contains(file->name)) + G_THROW( ERR_MSG("DjVmDir.dupl_name2") "\t" + file->name); + name2file[file->name]=file; + id2file[file->id]=file; + if (file->title.length()) + { + if (title2file.contains(file->title)) // duplicate titles may become ok some day + G_THROW( ERR_MSG("DjVmDir.dupl_title2") "\t" + file->title); + title2file[file->title]=file; + } + + // Make sure that there is no more than one file with shared annotations + if (file->is_shared_anno()) + { + for(GPosition pos=files_list;pos;++pos) + if (files_list[pos]->is_shared_anno()) + G_THROW( ERR_MSG("DjVmDir.multi_save2") ); + } + + // Add the file to the list + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos&&(cnt!=pos_num);++pos, cnt++) + continue; + if (pos) + files_list.insert_before(pos, file); + else + files_list.append(file); + + if (file->is_page()) + { + // This file is also a page + // Count its number + int page_num=0; + for(pos=files_list;pos;++pos) + { + GP<File> &f=files_list[pos]; + if (f==file) + break; + if (f->is_page()) + page_num++; + } + + int i; + page2file.resize(page2file.size()); + for(i=page2file.size()-1;i>page_num;i--) + page2file[i]=page2file[i-1]; + page2file[page_num]=file; + for(i=page_num;i<page2file.size();i++) + page2file[i]->page_num=i; + } + return pos_num; +} + +void +DjVmDir::delete_file(const GUTF8String &id) +{ + DEBUG_MSG("Deleting file with id='" << (const char *)id << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + for(GPosition pos=files_list;pos;++pos) + { + GP<File> & f=files_list[pos]; + if (id == f->id) + { + name2file.del(f->name); + id2file.del(f->id); + title2file.del(f->title); + if (f->is_page()) + { + for(int page=0;page<page2file.size();page++) + { + if (page2file[page]==f) + { + int i; + for(i=page;i<page2file.size()-1;i++) + page2file[i]=page2file[i+1]; + page2file.resize(page2file.size()-2); + for(i=page;i<page2file.size();i++) + page2file[i]->page_num=i; + break; + } + } + } + files_list.del(pos); + break; + } + } +} + +void +DjVmDir::set_file_name(const GUTF8String &id, const GUTF8String &name) +{ + DEBUG_MSG("DjVmDir::set_file_name(): id='" << id << "', name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + + // First see, if the name is unique + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->id!=id && file->name==name) + G_THROW( ERR_MSG("DjVmDir.name_in_use") "\t" + GUTF8String(name)); + } + + // Check if ID is valid + if (!id2file.contains(id, pos)) + G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id)); + GP<File> file=id2file[pos]; + name2file.del(file->name); + file->name=name; + name2file[name]=file; +} + +void +DjVmDir::set_file_title(const GUTF8String &id, const GUTF8String &title) +{ + DEBUG_MSG("DjVmDir::set_file_title(): id='" << id << "', title='" << title << "'\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GPosition pos; + + // First see, if the title is unique + for(pos=files_list;pos;++pos) + { + GP<File> file=files_list[pos]; + if (file->id!=id && file->title==title) + G_THROW( ERR_MSG("DjVmDir.title_in_use") "\t" + GUTF8String(title)); + } + + // Check if ID is valid + if (!id2file.contains(id, pos)) + G_THROW( ERR_MSG("DjVmDir.no_info") "\t" + GUTF8String(id)); + GP<File> file=id2file[pos]; + title2file.del(file->title); + file->title=title; + title2file[title]=file; +} + +GPList<DjVmDir::File> +DjVmDir::resolve_duplicates(const bool save_as_bundled) +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + // Make sure all the filenames are unique. + GPosition pos; + GMap<GUTF8String,void *> save_map; + GMap<GUTF8String,GPList<DjVmDir::File> > conflicts; + for(pos=files_list;pos;++pos) + { + const GUTF8String save_name=files_list[pos]->check_save_name(save_as_bundled).downcase(); + if(save_map.contains(save_name)) + { + conflicts[save_name].append(files_list[pos]); + }else + { + save_map[save_name]=0; + } + } + for(pos=conflicts;pos;++pos) + { + const GUTF8String &save_name=conflicts.key(pos); + const int dot=save_name.rsearch('.',0); + GPList<DjVmDir::File> &cfiles=conflicts[pos]; + int count=1; + for(GPosition qpos=cfiles;qpos;++qpos) + { + GUTF8String new_name=cfiles[qpos]->get_load_name(); + if((new_name != GUTF8String(GNativeString(new_name))) + ||conflicts.contains(new_name)) + { + do + { + new_name=(dot<0) + ?(save_name+"-"+GUTF8String(count++)) + :(save_name.substr(0,dot)+"-"+GUTF8String(count++)+ + save_name.substr(dot,(unsigned int)(-1))); + } while(save_map.contains(new_name.downcase())); + } + cfiles[qpos]->set_save_name(new_name); + save_map[new_name]=0; + } + } + return files_list; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir.h b/kviewshell/plugins/djvu/libdjvu/DjVmDir.h new file mode 100644 index 00000000..86b661e3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir.h @@ -0,0 +1,451 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir.h,v 1.10 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDIR_H +#define _DJVMDIR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVmDir.h + Files #"DjVmDir.h"# and #"DjVmDir.cpp"# implement class \Ref{DjVmDir} for + representing the directory of a DjVu multipage document. + + {\bf Bundled vs. Indirect format} --- There are currently two multipage + DjVu formats supported: {\em bundled} and {\em indirect}. In the first + format all component files composing a given document are packaged (or + bundled) into one file, in the second one every page and component is + stored in a separate file and there is one more file, which contains the + list of all others. + + {\bf Multipage DjVu format} --- Multipage DjVu documents follow the EA + IFF85 format (cf. \Ref{IFFByteStream.h}.) A document is composed of a + #"FORM:DJVM"# whose first chunk is a #"DIRM"# chunk containing the {\em + document directory}. This directory lists all component files composing + the given document, helps to access every component file and identify the + pages of the document. + \begin{itemize} + \item In a {\em bundled} multipage file, the component files + are stored immediately after the #"DIRM"# chunk, + within the #"FORM:DJVU"# composite chunk. + \item In an {\em indirect} multipage file, the component files are + stored in different files whose URLs are composed using information + stored in the #"DIRM"# chunk. + \end{itemize} + Most of the component files represent pages of a document. Some files + however represent data shared by several pages. The pages refer to these + supporting files by means of an inclusion chunk (#"INCL"# chunks) + identifying the supporting file. + + {\bf Document Directory} --- Every directory record describes a component + file. Each component file is identified by a small string named the + identifier (ID). Each component file also contains a file name and a + title. The format of the #"DIRM"# chunk is described in section + \Ref{Format of the DIRM chunk.}. + + Theoretically, IDs are used to uniquely identify each component file in + #"INCL"# chunks, names are used to compose the the URLs of the component + files in an indirect multipage DjVu file, and titles are cosmetic names + possibly displayed when viewing a page of a document. There are however + many problems with this scheme, and we {\em strongly suggest}, with the + current implementation to always make the file ID, the file name and the + file title identical. + + @memo Implements DjVu multipage document directory + @author Andrei Erofeev <[email protected]> + @version + #$Id: DjVmDir.h,v 1.10 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + + +#include "GString.h" +#include "GThreads.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** Implements DjVu multipage document directory. There are currently two + multipage DjVu formats supported: {\em bundled} and {\em indirect}. In + the first format all component files composing a given document are + packaged (or bundled) into one file, in the second one every page and + component is stored in a separate file and there is one more file, which + contains the list of all others. + + The multipage document directory lists all component files composing the + given document, helps to access every file, identify pages and maintain + user-specified shortcuts. Every directory record describes a file + composing the document. Each file is identified by a small string named + the identifier (ID). Each file may also contain a file name and a title. + + The #DjVmDir# class represents a multipage document directory. Its main + purpose is to encode and decode the document directory when writing or + reading the #DIRM# chunk. Normally you don't have to create this class + yourself. It's done automatically when \Ref{DjVmDoc} class initializes + itself. It may be useful though to be able to access records in the + directory because some classes (like \Ref{DjVuDocument} and \Ref{DjVmDoc}) + return a pointer to #DjVmDir# in some cases. */ + +class DjVmDir : public GPEnabled +{ +protected: + /** Class \Ref{DjVmDir::File} represents the directory records + managed by class \Ref{DjVmDir}. */ + DjVmDir(void) { } ; +public: + class File; + + static const int version; + + /** Class \Ref{DjVmDir::File} represents the directory records + managed by class \Ref{DjVmDir}. */ + static GP<DjVmDir> create(void) {return new DjVmDir; } ; + + /** Decodes the directory from the specified stream. */ + void decode(const GP<ByteStream> &stream); + /** Encodes the directory into the specified stream. */ + void encode(const GP<ByteStream> &stream, const bool do_rename=false) const; + /** Encodes the directory into the specified stream, explicitely as bundled or indirect. */ + void encode(const GP<ByteStream> &stream, const bool bundled, const bool do_rename) const; + /** Tests if directory defines an {\em indirect} document. */ + bool is_indirect(void) const; + /** Tests if the directory defines a {\em bundled} document. */ + bool is_bundled(void) const; + /** Translates page numbers to file records. */ + GP<File> page_to_file(int page_num) const; + /** Translates file names to file records. */ + GP<File> name_to_file(const GUTF8String & name) const; + /** Translates file IDs to file records. */ + GP<File> id_to_file(const GUTF8String &id) const; + /** Translates file shortcuts to file records. */ + GP<File> title_to_file(const GUTF8String &title) const; + /** Returns position of the file in the directory. */ + int get_file_pos(const File * f) const; + /** Returns position of the given page in the directory. */ + int get_page_pos(int page_num) const; + /** Check for duplicate names, and resolve them. */ + GPList<File> resolve_duplicates(const bool save_as_bundled); + /** Returns a copy of the list of file records. */ + GPList<File> get_files_list(void) const; + /** Returns the number of file records. */ + int get_files_num(void) const; + /** Returns the number of file records representing pages. */ + int get_pages_num(void) const; + /** Returns back pointer to the file with #SHARED_ANNO# flag. + Note that there may be only one file with shared annotations + in any multipage DjVu document. */ + GP<File> get_shared_anno_file(void) const; + /** Changes the title of the file with ID #id#. */ + void set_file_title(const GUTF8String &id, const GUTF8String &title); + /** Changes the name of the file with ID #id#. */ + void set_file_name(const GUTF8String &id, const GUTF8String &name); + /** Inserts the specified file record at the specified position. + Specifying #pos# equal to #-1# means to append. The actual position + inserted is returned. */ + int insert_file(const GP<File> & file, int pos=-1); + /** Removes a file record with ID #id#. */ + void delete_file(const GUTF8String &id); +private: + GCriticalSection class_lock; + GPList<File> files_list; + GPArray<File> page2file; + GPMap<GUTF8String, File> name2file; + GPMap<GUTF8String, File> id2file; + GPMap<GUTF8String, File> title2file; +private: //dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +class DjVmDir::File : public GPEnabled +{ +public: + // Out of the record: INCLUDE below must be zero and PAGE must be one. + // This is to avoid problems with the File constructor, which now takes + // 'int file_type' as the last argument instead of 'bool is_page' + + /** File type. Possible file types are: + \begin{description} + \item[PAGE] This is a top level page file. It may include other + #INCLUDE#d files, which may in turn be shared between + different pages. + \item[INCLUDE] This file is included into some other file inside + this document. + \item[THUMBNAILS] This file contains thumbnails for the document + pages. + \item[SHARED_ANNO] This file contains annotations shared by + all the pages. It's supposed to be included into every page + for the annotations to take effect. There may be only one + file with shared annotations in a document. + \end{description} */ + enum FILE_TYPE { INCLUDE=0, PAGE=1, THUMBNAILS=2, SHARED_ANNO=3 }; +protected: + /** Default constructor. */ + File(void); + +public: + static GP<File> create(void) { return new File(); } + static GP<File> create(const GUTF8String &load_name, + const GUTF8String &save_name, const GUTF8String &title, + const FILE_TYPE file_type); + + /** Check for filenames that are not valid for the native encoding, + and change them. */ + const GUTF8String &check_save_name(const bool as_bundled); + + /** File name. The optional file name must be unique and is the name + that will be used when the document is saved to an indirect file. + If not assigned, the value of #id# will be used for this purpose. + By keeping the name in {\em bundled} document we guarantee, that it + can be expanded later into {\em indirect} document and files will + still have the same names, if the name is legal on a given filesystem. + */ + const GUTF8String &get_save_name(void) const; + + /** File identifier. The encoder assigns a unique identifier to each file + in a multipage document. This is the name used when loading files. + Indirection chunks in other files (#"INCL"# chunks) may refer to another + file using its identifier. */ + const GUTF8String &get_load_name(void) const; + void set_load_name(const GUTF8String &id); + + /** File title. The file title is assigned by the user and may be used as + a shortcut for viewing a particular page. Names like #"chapter1"# or + #"appendix"# are appropriate. */ + const GUTF8String &get_title() const; + void set_title(const GUTF8String &id); + + /** Reports an ascii string indicating file type. */ + GUTF8String get_str_type(void) const; + + /** Offset of the file data in a bundled DJVM file. This number is + relevant in the {\em bundled} case only when everything is packed into + one single file. */ + int offset; + + /** Size of the file data in a bundled DJVM file. This number is + relevant in the {\em bundled} case only when everything is + packed into one single file. */ + int size; + + /** Have we checked the saved file name, to see if it is valid on the + local disk? */ + bool valid_name; + + /** Tests if this file represents a page of the document. */ + bool is_page(void) const + { + return (flags & TYPE_MASK)==PAGE; + } + + /** Returns #TRUE# if this file is included into some other files of + this document. */ + bool is_include(void) const + { + return (flags & TYPE_MASK)==INCLUDE; + } + + /** Returns #TRUE# if this file contains thumbnails for the document pages. */ + bool is_thumbnails(void) const + { + return (flags & TYPE_MASK)==THUMBNAILS; + } + + /** Returns the page number of this file. This function returns + #-1# if this file does not represent a page of the document. */ + bool is_shared_anno(void) const + { return (flags & TYPE_MASK)==SHARED_ANNO; } + + int get_page_num(void) const + { return page_num; } +protected: + GUTF8String name; + GUTF8String oldname; + GUTF8String id; + GUTF8String title; + void set_save_name(const GUTF8String &name); +private: + friend class DjVmDir; + enum FLAGS_0 { IS_PAGE_0=1, HAS_NAME_0=2, HAS_TITLE_0=4 }; + enum FLAGS_1 { HAS_NAME=0x80, HAS_TITLE=0x40, TYPE_MASK=0x3f }; + unsigned char flags; + int page_num; +}; + +inline const GUTF8String & +DjVmDir::File::get_load_name(void) const +{ return id; } + +inline const GUTF8String & +DjVmDir::File::get_title() const +{ return *(title.length()?&title:&id); } + +inline void +DjVmDir::File::set_title(const GUTF8String &xtitle) { title=xtitle; } + +/** @name Format of the DIRM chunk. + + {\bf Variants} --- There are two versions of the #"DIRM"# chunk format. + The version number is identified by the seven low bits of the first byte + of the chunk. Version {\bf 0} is obsolete and should never be used. This + section describes version {\bf 1}. There are two major multipage DjVu + formats supported: {\em bundled} and {\em indirect}. The #"DIRM"# chunk + indicates which format is used in the most significant bit of the first + byte of the chunk. The document is bundled when this bit is set. + Otherwise the document is indirect. + + {\bf Unencoded data} --- The #"DIRM"# chunk is composed some unencoded + data followed by \Ref{bzz} encoded data. The unencoded data starts with + the version byte and a 16 bit integer representing the number of component + files. All integers are encoded with the most significant byte first. + \begin{verbatim} + BYTE: Flags/Version: 0x<bundled>0000011 + INT16: Number of component files. + \end{verbatim} + When the document is a bundled document (i.e. the flag #bundled# is set), + this header is followed by the offsets of each of the component files within + the #"FORM:DJVM"#. These offsets allow for random component file access. + \begin{verbatim} + INT32: Offset of first component file. + INT32: Offset of second component file. + ... + INT32: Offset of last component file. + \end{verbatim} + + {\bf BZZ encoded data} --- The rest of the chunk is entirely compressed + with the BZZ general purpose compressor. We describe now the data fed + into (or retrieved from) the BZZ codec (cf. \Ref{BSByteStream}.) First + come the sizes and the flags associated with each component file. + \begin{verbatim} + INT24: Size of the first component file. + INT24: Size of the second component file. + ... + INT24: Size of the last component file. + BYTE: Flag byte for the first component file. + BYTE: Flag byte for the second component file. + ... + BYTE: Flag byte for the last component file. + \end{verbatim} + The flag bytes have the following format: + \begin{verbatim} + 0b<hasname><hastitle>000000 for a file included by other files. + 0b<hasname><hastitle>000001 for a file representing a page. + 0b<hasname><hastitle>000010 for a file containing thumbnails. + \end{verbatim} + Flag #hasname# is set when the name of the file is different from the file + ID. Flag #hastitle# is set when the title of the file is different from + the file ID. These flags are used to avoid encoding the same string three + times. Then come a sequence of zero terminated strings. There are one to + three such strings per component file. The first string contains the ID + of the component file. The second string contains the name of the + component file. It is only present when the flag #hasname# is set. The third + one contains the title of the component file. It is only present when the + flag #hastitle# is set. The \Ref{bzz} encoding system makes sure that + all these strings will be encoded efficiently despite their possible + redundancies. + \begin{verbatim} + ZSTR: ID of the first component file. + ZSTR: Name of the first component file (only if #hasname# is set.) + ZSTR: Title of the first component file (only if #hastitle# is set.) + ... + ZSTR: ID of the last component file. + ZSTR: Name of the last component file (only if #hasname# is set.) + ZSTR: Title of the last component file (only if #hastitle# is set.) + \end{verbatim} + + @memo Description of the format of the DIRM chunk. */ +//@} + + + +// -------------- IMPLEMENTATION + + +inline bool +DjVmDir::is_bundled(void) const +{ + return ! is_indirect(); +} + +inline bool +DjVmDir::is_indirect(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return ( files_list.size() && files_list[files_list] != 0 && + files_list[files_list]->offset==0 ); +} + + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp new file mode 100644 index 00000000..62694098 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp @@ -0,0 +1,169 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir0.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDir0.h" +#include "ByteStream.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +int +DjVmDir0::get_size(void) const + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + int size=0; + + size+=2; // number of files + for(int i=0;i<num2file.size();i++) + { + FileRec & file=*num2file[i]; + size+=file.name.length()+1; // file name + size+=1; // is IFF file + size+=4; // file offset + size+=4; // file size + }; + + return size; +} + +#ifndef NEED_DECODER_ONLY +void +DjVmDir0::encode(ByteStream & bs) + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + bs.write16(num2file.size()); + for(int i=0;i<num2file.size();i++) + { + FileRec & file=*num2file[i]; + bs.writestring(file.name); + bs.write8(0); + bs.write8(file.iff_file); + bs.write32(file.offset); + bs.write32(file.size); + } +} +#endif + +void +DjVmDir0::decode(ByteStream & bs) + // WARNING! make sure, that get_size(), encode() and decode() are in sync +{ + name2file.empty(); + num2file.empty(); + + for(int i=bs.read16();i>0;i--) + { + GUTF8String name; + char ch; + while(bs.read(&ch, 1) && ch) name+=ch; + bool iff_file=bs.read8()?true:false; + int offset=bs.read32(); + int size=bs.read32(); + add_file(name, iff_file, offset, size); + }; +} + +GP<DjVmDir0::FileRec> +DjVmDir0::get_file(const GUTF8String &name) +{ + if (name2file.contains(name)) + return name2file[name]; + return 0; +} + +GP<DjVmDir0::FileRec> +DjVmDir0::get_file(int file_num) +{ + if (file_num<num2file.size()) return num2file[file_num]; + return 0; +} + +void +DjVmDir0::add_file( + const GUTF8String &name, bool iff_file, int offset, int size) +{ + DEBUG_MSG("DjVmDir0::add_file(): name='" << name << "', iff=" << iff_file << + ", offset=" << offset << "\n"); + + if (name.search('/') >= 0) + G_THROW( ERR_MSG("DjVmDir0.no_slash") ); + + GP<FileRec> file=new FileRec(name, iff_file, offset, size); + name2file[name]=file; + num2file.resize(num2file.size()); + num2file[num2file.size()-1]=file; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h new file mode 100644 index 00000000..c17c795e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDir0.h @@ -0,0 +1,217 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDir0.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDIR0_H +#define _DJVMDIR0_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVmDir0.h + + Files #"DjVmDir0.h"# and #"DjVmDir0.cpp"# contain implementation of + \Ref{DjVmDir0} class responsible for reading and writing the directory + of a multipage all-in-one-file DjVu document (usually referred to as + {\bf DjVm} document. + + This is {\bf not} a navigation directory, which lists all the pages + in a multipage document. The navigation directory is supported by class + \Ref{DjVuNavDir}. This is the directory of a DjVm archive. + + + @memo Directory of DjVu all-in-one-file DjVu documents. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVmDir0.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ + +//@{ + +/** Directory of all-in-one-file DjVu documents (also known as DjVm documents). + This class can read and write the directory (table of contents, in other + words) of a DjVm document. This table of contents lists all {\bf files} + included into the document, not {\bf pages} like \Ref{DjVuNavDir} does. + It is normally stored in the document inside {\bf DIR0} chunk where + {\bf "0"} refers to the version number. + + An object of this class can be created either as a result of the decoding + of an existing DjVm file, or manually by calling the default constructor + and adding later directory entries by means of \Ref{add_file}() function. + + You normally will not want to create or decode this directory manually. + \Ref{DjVmFile} class will do it for you. */ +class DjVmDir0 : public GPEnabled +{ +public: + /** Describes a file record inside a DjVm document (archive) */ + class FileRec; +private: + GMap<GUTF8String, GP<FileRec> > name2file; + GPArray<FileRec> num2file; +protected: + /// Default constructor + DjVmDir0(void) {}; +public: + /// Copy constructor + DjVmDir0(const DjVmDir0 & d); + + static GP<DjVmDir0> create(void) {return new DjVmDir0;} + + virtual ~DjVmDir0(void) {}; + + /// Returns the number of files in the DjVm archive + int get_files_num(void) const; + + /// Returns the file record with name #name# + GP<FileRec> get_file(const GUTF8String &name); + + /// Returns the file record number #file_num# + GP<FileRec> get_file(int file_num); + + /** Creates a new file record with name #name# at offset + #offset# and size #size#, which is in IFF format if + #iff_file# is #TRUE#. */ + void add_file(const GUTF8String &name, bool iff_file, + int offset=-1, int size=-1); + + /// Returns the size of the directory if it were encoded in #DIR0# chunk + int get_size(void) const; + + /** Encodes the directory in #DIR0# chunk into the specified + \Ref{ByteStream} */ + void encode(ByteStream & bs); + + /** Decodes the directory from #DIR0# chunk from the specified + \Ref{ByteStream} */ + void decode(ByteStream & bs); + +}; + + /** Describes a file record inside a DjVm document (archive) */ +class DjVmDir0::FileRec : public GPEnabled +{ +public: + /// Name of the file. + GUTF8String name; + /// 1 if the file is in IFF format. + bool iff_file; + /// Offset of the file in the archive. + int offset; + /// Size of the file + int size; + + friend int operator==(const FileRec & f1, const FileRec & f2); + + /// Constructs the #FileRec# object + FileRec(const GUTF8String &name, bool iff_file, + int offset=-1, int size=-1); + /// Default constructor + FileRec(void); + virtual ~FileRec(void); +}; + +inline +DjVmDir0::FileRec::FileRec( + const GUTF8String &name_in, bool iff_file_in, int offset_in, int size_in) +: name(name_in), iff_file(iff_file_in), offset(offset_in), size(size_in) +{ +} + +inline +DjVmDir0::FileRec::FileRec(void) : iff_file(0), offset(-1), size(-1) +{ +} + +inline +DjVmDir0::FileRec::~FileRec(void) +{ +} + +inline int +DjVmDir0::get_files_num(void) const +{ + return num2file.size(); +} + +inline +DjVmDir0::DjVmDir0(const DjVmDir0 & d) : + name2file(d.name2file), num2file(d.num2file) +{ +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp new file mode 100644 index 00000000..5b851d6e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp @@ -0,0 +1,663 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDoc.cpp,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVmDoc.h" +#include "DjVmNav.h" +#include "DataPool.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static const char octets[4]={0x41,0x54,0x26,0x54}; + +// Save the file to disk, remapping INCL chunks while saving. +static void +save_file( + IFFByteStream &iff_in, IFFByteStream &iff_out, const DjVmDir &dir, + GMap<GUTF8String,GUTF8String> &incl) +{ + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid,true); + if(!chkid.cmp("FORM:",5)) + { + for(;iff_in.get_chunk(chkid);iff_in.close_chunk()) + { + iff_out.put_chunk(chkid); + if(chkid == "INCL") + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + GPosition pos=incl.contains(incl_str); + if(pos) + { + iff_out.get_bytestream()->writestring(incl[pos]); + }else + { + GP<DjVmDir::File> incl_file=dir.id_to_file(incl_str); + if(incl_file) + { + DEBUG_MSG("INCL '"<<(const char *)incl_file->get_save_name()<<"'\n"); + const GUTF8String incl_name=incl_file->get_save_name(); + incl[incl_str]=incl_name; + iff_out.get_bytestream()->writestring(incl_name); + }else + { + DEBUG_MSG("BOGUS INCL '"<<(const char *)incl_str<<"'\n"); + iff_out.copy(*iff_in.get_bytestream()); + } + } + }else + { + iff_out.copy(*iff_in.get_bytestream()); + } + iff_out.close_chunk(); + } + }else + { + iff_out.copy(*iff_in.get_bytestream()); + } + iff_out.close_chunk(); + iff_in.close_chunk(); + } +} + +DjVmDoc::DjVmDoc(void) +{ + DEBUG_MSG("DjVmDoc::DjVmDoc(): Constructing empty DjVm document.\n"); + DEBUG_MAKE_INDENT(3); +} + +void +DjVmDoc::init(void) +{ + dir=DjVmDir::create(); +} + +GP<DjVmDoc> +DjVmDoc::create(void) +{ + DjVmDoc *doc=new DjVmDoc(); + GP<DjVmDoc> retval=doc; + doc->init(); + return retval; +} + +void +DjVmDoc::insert_file(const GP<DjVmDir::File> & f, + GP<DataPool> data_pool, int pos) +{ + DEBUG_MSG("DjVmDoc::insert_file(): inserting file '" << f->get_load_name() << + "' at pos " << pos << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!f) + G_THROW( ERR_MSG("DjVmDoc.no_zero_file") ); + if (data.contains(f->get_load_name())) + G_THROW( ERR_MSG("DjVmDoc.no_duplicate") ); + + char buffer[4]; + if (data_pool->get_data(buffer, 0, 4)==4 && !memcmp(buffer, octets, 4)) + { + data_pool=DataPool::create(data_pool, 4, -1); + } + data[f->get_load_name()]=data_pool; + dir->insert_file(f, pos); +} + +void +DjVmDoc::insert_file( + ByteStream &data, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, const GUTF8String &title, + int pos) +{ + const GP<DjVmDir::File> file( + DjVmDir::File::create(name, id, title, file_type)); + const GP<DataPool> pool(DataPool::create()); + // Cannot connect to a bytestream. + // Must copy data into the datapool. + int nbytes; + char buffer[1024]; + while ((nbytes = data.read(buffer, sizeof(buffer)))) + pool->add_data(buffer, nbytes); + pool->set_eof(); + // Call low level insert + insert_file(file, pool, pos); +} + +void +DjVmDoc::insert_file( + const GP<DataPool> &pool, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, const GUTF8String &title, + int pos) +{ + const GP<DjVmDir::File> file( + DjVmDir::File::create(name, id, title, file_type)); + // Call low level insert + insert_file(file, pool, pos); +} + +void +DjVmDoc::delete_file(const GUTF8String &id) +{ + DEBUG_MSG("DjVmDoc::delete_file(): deleting file '" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!data.contains(id)) + G_THROW(GUTF8String( ERR_MSG("DjVmDoc.cant_delete") "\t") + id); + + data.del(id); + dir->delete_file(id); +} + +void +DjVmDoc::set_djvm_nav(GP<DjVmNav> n) +{ + if (n && ! n->isValidBookmark()) + G_THROW("Invalid bookmark data"); + nav = n; +} + +GP<DataPool> +DjVmDoc::get_data(const GUTF8String &id) const +{ + GPosition pos; + if (!data.contains(id, pos)) + G_THROW(GUTF8String( ERR_MSG("DjVmDoc.cant_find") "\t") + id); + const GP<DataPool> pool(data[pos]); + // First check that the file is in IFF format + G_TRY + { + const GP<ByteStream> str_in(pool->get_stream()); + const GP<IFFByteStream> giff_in=IFFByteStream::create(str_in); + IFFByteStream &iff_in=*giff_in; + GUTF8String chkid; + int size=iff_in.get_chunk(chkid); + if (size<0 || size>0x7fffffff) + G_THROW( ERR_MSG("DjVmDoc.not_IFF") "\t" + id); + } + G_CATCH_ALL + { + G_THROW( ERR_MSG("DjVmDoc.not_IFF") "\t" + id); + } + G_ENDCATCH; + return pool; +} + +void +DjVmDoc::write(const GP<ByteStream> &gstr) +{ + const GMap<GUTF8String,void *> reserved; + write(gstr,reserved); +} + +static inline GUTF8String +get_name(const DjVmDir::File &file) +{ + const GUTF8String save_name(file.get_save_name()); + return save_name.length()?save_name:(file.get_load_name()); +} + +void +DjVmDoc::write(const GP<ByteStream> &gstr, + const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVmDoc::write(): Storing document into the byte stream.\n"); + DEBUG_MAKE_INDENT(3); + + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(true); + bool do_rename=false; + GPosition pos(reserved); + + GMap<GUTF8String,GUTF8String> incl; + DEBUG_MSG("pass 1: looking for reserved names."); + if(pos) + { + // Check if there are any conflicting file names. + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + if((do_rename=(reserved.contains(file->get_load_name())?true:false)) + ||(do_rename=(reserved.contains(file->get_save_name())?true:false))) + { + break; + } + } + // If there are conflicting file names, check if the save names + // are OK. If not, generate new save names. + if(do_rename) + { + DEBUG_MSG("pass 1: renaming reserved names."); + for(;;files_list=dir->resolve_duplicates(true)) + { + GMap<GUTF8String,void *> this_doc; + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + this_doc[::get_name(*file)]=0; + } + bool need_new_list=false; + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + const GUTF8String name(::get_name(*file)); + if(reserved.contains(name)) + { + GUTF8String new_name; + int series=0; + do + { + int dot=name.rsearch('.'); + if(dot>0) + { + new_name=name.substr(0,dot)+ + "_"+GUTF8String(++series)+name.substr(dot,-1); + }else + { + new_name=name+"_"+GUTF8String(++series); + } + } while(reserved.contains(new_name)||this_doc.contains(new_name)); + dir->set_file_name(file->get_load_name(),new_name); + need_new_list=true; + } + } + if(!need_new_list) + break; + } + } + } + + DEBUG_MSG("pass 2: create dummy DIRM chunk and calculate offsets...\n"); + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + file->offset=0xffffffff; + GPosition data_pos=data.contains(file->get_load_name()); + if (!data_pos) + G_THROW( ERR_MSG("DjVmDoc.no_data") "\t" + file->get_load_name()); + if(do_rename) + { + GP<ByteStream> gout(ByteStream::create()); + { + const GP<IFFByteStream> giff_in( + IFFByteStream::create(data[data_pos]->get_stream())); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gout)); + ::save_file(*giff_in,*giff_out,*dir,incl); + } + gout->seek(0L); + data[data_pos]=DataPool::create(gout); + } + file->size=data[data_pos]->get_length(); + if (!file->size) + G_THROW( ERR_MSG("DjVmDoc.zero_file") ); + } + + const GP<ByteStream> tmp_str(ByteStream::create()); + const GP<IFFByteStream> gtmp_iff(IFFByteStream::create(tmp_str)); + IFFByteStream &tmp_iff=*gtmp_iff; + tmp_iff.put_chunk("FORM:DJVM", 1); + tmp_iff.put_chunk("DIRM"); + dir->encode(tmp_iff.get_bytestream(),do_rename); + tmp_iff.close_chunk(); + if (nav) + { + tmp_iff.put_chunk("NAVM"); + nav->encode(tmp_iff.get_bytestream()); + tmp_iff.close_chunk(); + } + tmp_iff.close_chunk(); + int offset=tmp_iff.tell(); + + for(pos=files_list;pos;++pos) + { + if ((offset & 1)!=0) + offset++; + + GP<DjVmDir::File> & file=files_list[pos]; + file->offset=offset; + offset+=file->size; // file->size has been set in the first pass + } + + DEBUG_MSG("pass 3: store the file contents.\n"); + + GP<IFFByteStream> giff=IFFByteStream::create(gstr); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + dir->encode(iff.get_bytestream(),do_rename); + iff.close_chunk(); + if (nav) + { + iff.put_chunk("NAVM"); + nav->encode(iff.get_bytestream()); + iff.close_chunk(); + } + + for(pos=files_list;pos;++pos) + { + GP<DjVmDir::File> & file=files_list[pos]; + + const GP<DataPool> pool=get_data(file->get_load_name()); + const GP<ByteStream> str_in(pool->get_stream()); + if ((iff.tell() & 1)!=0) + { + iff.get_bytestream()->write8(0); + } + iff.copy(*str_in); + } + + iff.close_chunk(); + iff.flush(); + + DEBUG_MSG("done storing DjVm file.\n"); +} + +void +DjVmDoc::read(const GP<DataPool> & pool) +{ + DEBUG_MSG("DjVmDoc::read(): reading the BUNDLED doc contents from the pool\n"); + DEBUG_MAKE_INDENT(3); + + const GP<ByteStream> str(pool->get_stream()); + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:DJVM") + G_THROW( ERR_MSG("DjVmDoc.no_form_djvm") ); + + iff.get_chunk(chkid); + if (chkid!="DIRM") + G_THROW( ERR_MSG("DjVmDoc.no_dirm_chunk") ); + dir->decode(iff.get_bytestream()); + iff.close_chunk(); + + data.empty(); + + if (dir->is_indirect()) + G_THROW( ERR_MSG("DjVmDoc.cant_read_indr") ); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + DjVmDir::File * f=files_list[pos]; + + DEBUG_MSG("reading contents of file '" << f->get_load_name() << "'\n"); + data[f->get_load_name()]=DataPool::create(pool, f->offset, f->size); + } +} + +void +DjVmDoc::read(ByteStream & str_in) +{ + DEBUG_MSG("DjVmDoc::read(): reading the BUNDLED doc contents from the stream\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> pool=DataPool::create(); + char buffer[1024]; + int length; + while((length=str_in.read(buffer, 1024))) + pool->add_data(buffer, length); + pool->set_eof(); + + read(pool); +} + +void +DjVmDoc::read(const GURL &url) +{ + DEBUG_MSG("DjVmDoc::read(): reading the doc contents from the HDD\n"); + DEBUG_MAKE_INDENT(3); + + GP<DataPool> pool=DataPool::create(url); + const GP<ByteStream> str(pool->get_stream()); + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:DJVM") + G_THROW( ERR_MSG("DjVmDoc.no_form_djvm2") ); + + iff.get_chunk(chkid); + if (chkid!="DIRM") + G_THROW( ERR_MSG("DjVmDoc.no_dirm_chunk") ); + dir->decode(iff.get_bytestream()); + iff.close_chunk(); + + if (dir->is_bundled()) + read(pool); + else + { +// GUTF8String full_name=GOS::expand_name(name); +// GUTF8String dir_name=GOS::dirname(GOS::url_to_filename(url.base())); + GURL dirbase=url.base(); + + data.empty(); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + DjVmDir::File * f=files_list[pos]; + + DEBUG_MSG("reading contents of file '" << f->get_load_name() << "'\n"); + + const GURL::UTF8 url(f->get_load_name(),dirbase); + data[f->get_load_name()]=DataPool::create(url); + } + } +} + +void +DjVmDoc::write_index(const GP<ByteStream> &str) +{ + DEBUG_MSG("DjVmDoc::write_index(): Storing DjVm index file\n"); + DEBUG_MAKE_INDENT(3); + + GPList<DjVmDir::File> files_list=dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> file=files_list[pos]; + file->offset=0; + + GPosition data_pos=data.contains(file->get_load_name()); + if (!data_pos) + G_THROW( ERR_MSG("DjVmDoc.no_data") "\t" + file->get_load_name()); + file->size=data[data_pos]->get_length(); + if (!file->size) + G_THROW( ERR_MSG("DjVmDoc.zero_file") ); + } + + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + dir->encode(iff.get_bytestream()); + iff.close_chunk(); + if (nav) + { + iff.put_chunk("NAVM"); + nav->encode(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + iff.flush(); +} + +void +DjVmDoc::save_page( + const GURL &codebase, const DjVmDir::File &file) const +{ + GMap<GUTF8String,GUTF8String> incl; + save_file(codebase,file,&incl); +} + +void +DjVmDoc::save_page( + const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl ) const +{ + save_file(codebase,file,&incl); +} + +void +DjVmDoc::save_file( + const GURL &codebase, const DjVmDir::File &file) const +{ + save_file(codebase,file,0); +} + +GUTF8String +DjVmDoc::save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl, const GP<DataPool> &pool) const +{ + const GUTF8String save_name(file.get_save_name()); + const GURL::UTF8 new_url(save_name,codebase); + DEBUG_MSG("storing file '"<<new_url<<"'\n"); + DataPool::load_file(new_url); + const GP<ByteStream> str_in(pool->get_stream()); + const GP<ByteStream> str_out(ByteStream::create(new_url, "wb")); + ::save_file( *IFFByteStream::create(str_in), + *IFFByteStream::create(str_out), *dir, incl); + return save_name; +} + +void +DjVmDoc::save_file( + const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> *incl) const +{ + const GUTF8String load_name=file.get_load_name(); + if(!incl || !incl->contains(load_name)) + { + GMap<GUTF8String,GUTF8String> new_incl; + const GUTF8String save_name( + save_file(codebase,file,new_incl,get_data(load_name))); + + if(incl) + { + (*incl)[load_name]=save_name; + for(GPosition pos=new_incl;pos;++pos) + { + save_file(codebase,file,incl); + } + } + } +} + +void +DjVmDoc::expand(const GURL &codebase, const GUTF8String &idx_name) +{ + DEBUG_MSG("DjVmDoc::expand(): Expanding into '" << codebase << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Resolve any name conflicts + // Find the list of all files. + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false); + + // store each file + for(GPosition pos=files_list;pos;++pos) + { + save_file(codebase,*files_list[pos]); + } + + if (idx_name.length()) + { + const GURL::UTF8 idx_url(idx_name, codebase); + + DEBUG_MSG("storing index file '" << idx_url << "'\n"); + + DataPool::load_file(idx_url); + GP<ByteStream> str=ByteStream::create(idx_url, "wb"); + write_index(str); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h new file mode 100644 index 00000000..637d0b78 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmDoc.h @@ -0,0 +1,274 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmDoc.h,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMDOC_H +#define _DJVMDOC_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVmDir.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class DataPool; +class GURL; +class GUTF8String; +class DjVmNav; + +/** @name DjVmDoc.h + Files #"DjVmDoc.h"# and #"DjVmDoc.cpp"# contain implementation of the + \Ref{DjVmDoc} class used to read and write new DjVu multipage documents. + + @memo DjVu multipage documents reader/writer. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVmDoc.h,v 1.10 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** Read/Write DjVu multipage documents. + + The "new" DjVu multipage documents can be of two types: {\em bundled} and + {\em indirect}. In the first case all pages are packed into one file, + which is very like an archive internally. In the second case every page + is stored in a separate file. Plus there can be other components, + included into one or more pages, which also go into separate files. In + addition to pages and components, in the case of the {\em indirect} format + there is one more top-level file with the document directory (see + \Ref{DjVmDir}), which is basically an index file containing the + list of all files composing the document. + + This class can read documents of both formats and can save them under any + format. It is therefore ideal for converting between {\em bundled} and + {\em indirect} formats. It cannot be used however for reading obsolete + formats. The best way to convert obsolete formats consists in reading + them with class \Ref{DjVuDocument} class and saving them using + \Ref{DjVuDocument::write} or \Ref{DjVuDocument::expand}. + + This class can also be used to create and modify multipage documents at + the low level without decoding every page or component (See + \Ref{insert_file}() and \Ref{delete_file}()). +*/ + +class DjVmDoc : public GPEnabled +{ + // Internal function. +protected: + DjVmDoc(void); + void init(void); +public: + /// Creator + static GP<DjVmDoc> create(void); + /** Inserts a file into the document. + @param data ByteStream containing the file data. + @param file_type Describes the type of the file to be inserted. + See \Ref{DjVmDir::File} for details. + @param name Name of the file in the document (e.g. an URL). + @param id Identifier of the file (as used in INCL chunks). + @param title Optional title of the file (shown in browsers). + @param pos Position of the file in the document (default is append). + */ + void insert_file( + ByteStream &data, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, + const GUTF8String &title=GUTF8String(), int pos=-1 ); + /** Inserts a file into the document. + @param pool Data pool containing file data. + @param file_type Describes the type of the file to be inserted. + See \Ref{DjVmDir::File} for details. + @param name Name of the file in the document (e.g. an URL). + @param id Identifier of the file (as used in INCL chunks). + @param title Optional title of the file (shown in browsers). + @param pos Position of the file in the document (default is append). + */ + void insert_file( + const GP<DataPool> &pool, DjVmDir::File::FILE_TYPE file_type, + const GUTF8String &name, const GUTF8String &id, + const GUTF8String &title=GUTF8String(), int pos=-1 ); + + /** Inserts a file described by \Ref{DjVmDir::File} structure with + data #data# at position #pos#. If #pos# is negative, the file + will be appended to the document. Otherwise it will be inserted + at position #pos#. */ + void insert_file(const GP<DjVmDir::File> & f, + GP<DataPool> data, int pos=-1); + + /** Removes file with the specified #id# from the document. Every + file inside a new DjVu multipage document has its unique ID + (refer to \Ref{DjVmDir} for details), which is passed to this + function. */ + void delete_file(const GUTF8String &id); + + /** Set the bookmarks */ + void set_djvm_nav(GP<DjVmNav> n); + + /** Returns the directory of the DjVm document (the one which will + be encoded into #DJVM# chunk of the top-level file or the bundle). */ + GP<DjVmDir> get_djvm_dir(void); + + /** Returns contents of file with ID #id# from the document. + Please refer to \Ref{DjVmDir} for the explanation of what + IDs mean. */ + GP<DataPool> get_data(const GUTF8String &id) const; + + /** Reading routines */ + //@{ + /** Reads contents of a {\em bundled} multipage DjVu document from + the stream. */ + void read(ByteStream & str); + /** Reads contents of a {\em bundled} multipage DjVu document from + the \Ref{DataPool}. */ + void read(const GP<DataPool> & data_pool); + /** Reads the DjVu multipage document in either {\em bundled} or + {\em indirect} format. + + {\bf Note:} For {\em bundled} documents the file is not + read into memory. We just open it and access data directly there. + Thus you should not modify the file contents. + + @param name For {\em bundled} documents this is the name + of the document. For {\em indirect} documents this is + the name of the top-level file of the document (containing + the \Ref{DjVmDir} with the list of all files). + The rest of the files are expected to be in the + same directory and will be read by this function as well. */ + void read(const GURL &url); + //@} + + /** Writing routines */ + //@{ + /** Writes the multipage DjVu document in the {\em bundled} format into + the stream. */ + void write(const GP<ByteStream> &str); + /** Writes the multipage DjVu document in the {\em bundled} format into + the stream, reserving any of the specified names. */ + void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *>& reserved); + /** Stored index (top-level) file of the DjVu document in the {\em + indirect} format into the specified stream. */ + void write_index(const GP<ByteStream> &str); + /** Writes the multipage DjVu document in the {\em indirect} format + into the given directory. Every page and included file will be + stored as a separate file. Besides, one top-level file with + the document directory (named #idx_name#) will be created unless + #idx_name# is empty. + + @param dir_name Name of the directory where files should be + created + @param idx_name Name of the top-level file with the \Ref{DjVmDir} + with the list of files composing the given document. + If empty, the file will not be created. */ + void expand(const GURL &codebase, const GUTF8String &idx_name); + + /** Writes an individual file, and all included files. + INCL chunks will be remapped as appropriate. */ + void save_page(const GURL &codebase, const DjVmDir::File &file) const; + + /** Writes an individual file if not mapped, and all included files. + INCL chunks will be remapped as appropriate. All pages saved + are added to the #incl# map. */ + void save_page(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl) const; + + /** Writes an individual file specified, remapping INCL chunks as + appropriate. Included files will not be saved. */ + void save_file(const GURL &codebase, const DjVmDir::File &file) const; + + /** Writes the specified file from the given #pool#. */ + GUTF8String save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> &incl, + const GP<DataPool> &pool) const; + //@} +private: + void save_file(const GURL &codebase, const DjVmDir::File &file, + GMap<GUTF8String,GUTF8String> *incl) const; + GP<DjVmDir> dir; + GP<DjVmNav> nav; + GPMap<GUTF8String, DataPool > data; +private: // dummy stuff + static void write(ByteStream *); + static void write_index(ByteStream *); +}; + +inline GP<DjVmDir> +DjVmDoc::get_djvm_dir(void) +{ + return dir; +} + + +//@} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp b/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp new file mode 100644 index 00000000..9e8b5fd7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp @@ -0,0 +1,338 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmNav.cpp,v 1.1 2005/05/25 17:36:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include <ctype.h> + +#include "DjVuDocument.h" +#include "DjVmNav.h" +#include "BSByteStream.h" +#include "GURL.h" +#include "debug.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GP<DjVmNav::DjVuBookMark> +DjVmNav::DjVuBookMark::create(void) +{ + return new DjVuBookMark(); +} + +GP<DjVmNav::DjVuBookMark> +DjVmNav::DjVuBookMark::create(const unsigned short count, + const GUTF8String &displayname, + const GUTF8String &url) +{ + DjVuBookMark *pvm=new DjVuBookMark(); + GP<DjVuBookMark> bookmark=pvm; + pvm->count=count; + pvm->displayname=displayname; + pvm->url=url; + return bookmark; +} + +DjVmNav::DjVuBookMark::DjVuBookMark(void) + : count(0), displayname(), url() +{ +} + +GP<DjVmNav> +DjVmNav::create(void) +{ + return new DjVmNav; +} + +// Decode the input bytestream and populate this object +void +DjVmNav::DjVuBookMark::decode(const GP<ByteStream> &gstr) +{ + int textsize=0, readsize=0; + char *buffer=0; + ByteStream &bs=*gstr; + count = bs.read8(); + displayname.empty(); +#ifdef DJVMNAV_WITH_256LIMIT + textsize = bs.read24(); +#else + int counthi = bs.read8(); + count = (counthi<<8)+ count; + textsize = bs.read16(); +#endif + if (textsize) + { + buffer = displayname.getbuf(textsize); + readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + } + url.empty(); + textsize = bs.read24(); + if (textsize) + { + buffer = url.getbuf(textsize); + readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + } +} + +// Serialize this object to the output bytestream +void +DjVmNav::DjVuBookMark::encode(const GP<ByteStream> &gstr) +{ + int textsize=0; + ByteStream &bs=*gstr; +#ifdef DJVMNAV_WITH_256LIMIT + if (count>255) + G_THROW("Excessive number of children in bookmark tree"); + bs.write8(count); + textsize = displayname.length(); + bs.write24( textsize ); +#else + if (count>65535) + G_THROW("Excessive number of children in bookmark tree"); + bs.write8( count & 0xff ); + bs.write8( (count>>8) & 0xff ); + textsize = displayname.length(); + bs.write16( textsize ); +#endif + bs.writestring(displayname); + textsize = url.length(); + bs.write24( textsize ); + bs.writestring(url); +} + +// Text dump of this object to the output bytestream +void +DjVmNav::DjVuBookMark::dump(const GP<ByteStream> &gstr) +{ + int textsize=0; + ByteStream &bs=*gstr; + bs.format("\n count=%d\n",count); + textsize = displayname.length(); + bs.format(" (%d) %s\n",textsize, displayname.getbuf()); + textsize = url.length(); + bs.format(" (%d) %s\n",textsize, url.getbuf()); +} + +// Decode the input bytestream and populate this object +void +DjVmNav::decode(const GP<ByteStream> &gstr) +{ + //ByteStream &str=*gstr; + GP<ByteStream> gpBSByteStream = BSByteStream::create(gstr); + GCriticalSectionLock lock(&class_lock); + bookmark_list.empty(); + int nbookmarks=gpBSByteStream->read16(); + if (nbookmarks) + { + for(int bookmark=0;bookmark<nbookmarks;bookmark++) + { + GP<DjVuBookMark> pBookMark=DjVuBookMark::create(); + pBookMark->decode(gpBSByteStream); + bookmark_list.append(pBookMark); + } + } +} + +// Serialize this object to the output stream +void +DjVmNav::encode(const GP<ByteStream> &gstr) +{ + //ByteStream &str=*gstr; + GP<ByteStream> gpBSByteStream = BSByteStream::create(gstr, 1024); + GCriticalSectionLock lock(&class_lock); + int nbookmarks=bookmark_list.size(); + gpBSByteStream->write16(nbookmarks); + if (nbookmarks) + { + GPosition pos; + int cnt=0; + for (pos = bookmark_list; pos; ++pos) + { + bookmark_list[pos]->encode(gpBSByteStream); + cnt++; + } + if (nbookmarks != cnt) + { + GUTF8String msg; + msg.format("Corrupt bookmarks found during encode: %d of %d \n", + cnt, nbookmarks); + G_THROW (msg); + } + } +} + +int +DjVmNav::getBookMarkCount() +{ + return(bookmark_list.size()); +} + +void +DjVmNav::append (const GP<DjVuBookMark> &gpBookMark) +{ + bookmark_list.append(gpBookMark); +} + +bool +DjVmNav::getBookMark(GP<DjVuBookMark> &gpBookMark, int iPos) +{ + GPosition pos = bookmark_list.nth(iPos); + if (pos) + gpBookMark = bookmark_list[pos]; + else + gpBookMark = 0; + return (gpBookMark?true:false); +} + + +// A text dump of this object +void +DjVmNav::dump(const GP<ByteStream> &gstr) +{ + ByteStream &str=*gstr; + GCriticalSectionLock lock(&class_lock); + int nbookmarks=bookmark_list.size(); + str.format("%d bookmarks:\n",nbookmarks); + if (nbookmarks) + { + GPosition pos; + int cnt=0; + for (pos = bookmark_list; pos; ++pos) + { + bookmark_list[pos]->dump(&str); + cnt++; + } + if (nbookmarks != cnt) + { + GUTF8String msg; + msg.format("Corrupt bookmarks found during encode: %d of %d \n", + cnt,nbookmarks); + G_THROW (msg); + } + } +} + +bool +DjVmNav::isValidBookmark() +{ + //test if the bookmark is properly given + //for example: (4, "A", urla) + // (0, "B", urlb) + // (0, "C", urlc) + //is not a bookmark since A suppose to have 4 decendents, it only get one. + int bookmark_totalnum=getBookMarkCount(); + GP<DjVuBookMark> gpBookMark; + int* count_array=(int*)malloc(sizeof(int)*bookmark_totalnum); + for(int i=0;i<bookmark_totalnum;i++) + { + getBookMark(gpBookMark, i); + count_array[i]=gpBookMark->count; + } + int index=0; + int trees=0; + int* treeSizes=(int*)malloc(sizeof(int)*bookmark_totalnum); + while(index<bookmark_totalnum) + { + int treeSize=get_tree(index,count_array,bookmark_totalnum); + if(treeSize>0) //is a tree + { + index+=treeSize; + treeSizes[trees++]=treeSize; + } + else //not a tree + break; + } + free(count_array); + free(treeSizes); + return true; +} + +int +DjVmNav::get_tree(int index, int* count_array, int count_array_size) +{ + int i=index; + int accumulate_count=0; + while(i<count_array_size) + { + accumulate_count+=count_array[i]; + if(accumulate_count==0) + return 1; + else if(accumulate_count == i-index) //get a tree + return accumulate_count; + i++; + } + return 0; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVmNav.h b/kviewshell/plugins/djvu/libdjvu/DjVmNav.h new file mode 100644 index 00000000..46c5d57f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVmNav.h @@ -0,0 +1,146 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVmNav.h,v 1.1 2005/05/25 17:36:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVMNAV_H +#define _DJVMNAV_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "DjVuGlobal.h" +#include "GString.h" +#include "GThreads.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class ByteStream; + +/** The NAVM chunk. + The optional #"NAVM"# chunk which follows the DIRM chunk describes + how the user can navigate the document. + This is a list of DjVuBookMarks. +**/ + +class DjVmNav : public GPEnabled +{ +public: + /** Class \Ref{DjVmNav::DjVuBookMark} represents a entry in the + hierarchy of contents. */ + class DjVuBookMark; + + static GP<DjVmNav> create(void); + /** Decodes the directory from the specified stream. */ + void decode(const GP<ByteStream> &stream); + /** Encodes the directory into the specified stream. */ + void encode(const GP<ByteStream> &stream) ; + void dump(const GP<ByteStream> &stream) ; + /** Return bookmark at zero-based position i */ + bool getBookMark(GP<DjVuBookMark> &gpBookMark, int i) ; + int getBookMarkCount(); + /** Append the BookMark to the end of the list */ + void append (const GP<DjVuBookMark> &gpBookMark) ; + /** This function will check the given bookmark is valid or not */ + bool isValidBookmark(); + /** This function determines if the given count_array is a tree + sequence, that is if it fits a tree. */ + int get_tree(int index, int* count_array, int count_array_size); +protected: + DjVmNav(void) { } ; +private: + GCriticalSection class_lock; + GPList<DjVuBookMark> bookmark_list; +}; + +/** The DjVuBookMark. + Each entry in the Navigation chunk (NAVM) is a bookmark. A bookmark + contains a count of immediate children, a display string and a url. +**/ + +class DjVmNav::DjVuBookMark : public GPEnabled +{ +protected: + /** Default constructor. */ + DjVuBookMark(void); +public: + static GP<DjVuBookMark> create(void); + static GP<DjVuBookMark> create(const unsigned short count, + const GUTF8String &displayname, + const GUTF8String &url); + void encode(const GP<ByteStream> &stream); + void dump(const GP<ByteStream> &stream); + void decode(const GP<ByteStream> &stream); + int count; // count of immediate children. + GUTF8String displayname; // example: "Section 3.5 - Encryption" + GUTF8String url; // url, may be blank or relative. +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp new file mode 100644 index 00000000..a6772e61 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp @@ -0,0 +1,1514 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuAnno.cpp,v 1.12 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuAnno.h" +#include "GContainer.h" +#include "GException.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "GMapAreas.h" + +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// GLParser.h and GLParser.cpp used to be separate files capable to decode +// that weird ANTa chunk format into C++ structures and lists. But since +// its implementation is temporary and is used only in this file (DjVuAnno.cpp) +// it appears reasonable to build it in here. + +//*************************************************************************** +//****************************** GLParser.h ********************************* +//*************************************************************************** + + +class GLObject : public GPEnabled +{ +public: + enum GLObjectType { INVALID=0, NUMBER=1, STRING=2, SYMBOL=3, LIST=4 }; + static const char * const GLObjectString[LIST+1]; + + GLObject(int _number=0); + GLObject(GLObjectType type, const char * str); + GLObject(const char * name, const GPList<GLObject> & list); + virtual ~GLObject(void); + + int get_number(void) const; + GUTF8String get_string(void) const; + GUTF8String get_symbol(void) const; + GPList<GLObject> & get_list(void); + GP<GLObject> operator[](int n) const; + + GLObjectType get_type(void) const; + GUTF8String get_name(void) const; + void print(ByteStream & str, int compact=1, int indent=0, int * cur_pos=0) const; +private: + GLObjectType type; + GUTF8String name; + + int number; + GUTF8String string; + GUTF8String symbol; + GPList<GLObject> list; + void throw_can_not_convert_to(const GLObjectType to) const; +}; + +const char * const GLObject::GLObjectString[]= + {"invalid", "number", "string", "symbol", "list"}; + +inline GLObject::GLObjectType +GLObject::get_type(void) const { return type; } + +inline +GLObject::~GLObject(void) {} + +class GLToken +{ +public: + enum GLTokenType { OPEN_PAR, CLOSE_PAR, OBJECT }; + GLTokenType type; + GP<GLObject> object; + + GLToken(GLTokenType type, const GP<GLObject> & object); +}; + +inline +GLToken::GLToken(GLTokenType xtype, const GP<GLObject> & xobject) : + type(xtype), object(xobject) {} + +class GLParser +{ +public: + void parse(const char * str); + GPList<GLObject> & get_list(void); + GP<GLObject> get_object(const char * name, bool last=true); + void print(ByteStream & str, int compact=1); + + GLParser(void); + GLParser(const char * str); + ~GLParser(void); +private: + GPList<GLObject> list; + + bool compat; + void skip_white_space(const char * & start); + void check_compat(const char *str); + GLToken get_token(const char * & start); + void parse(const char * cur_name, GPList<GLObject> & list, + const char * & start); +}; + +GLParser::GLParser(void) + : compat(false) +{ +} + +GLParser::~GLParser(void) +{ +} + +GPList<GLObject> & +GLParser::get_list(void) +{ + return list; +} + +GLParser::GLParser(const char * str) + : compat(false) +{ + parse(str); +} + + +//*************************************************************************** +//***************************** GLParser.cpp ******************************** +//*************************************************************************** + + +GLObject::GLObject(int xnumber) : type(NUMBER), number(xnumber) {} + +GLObject::GLObject(GLObjectType xtype, const char * str) : type(xtype) +{ + if (type!=STRING && type!=SYMBOL) + G_THROW( ERR_MSG("DjVuAnno.bad_type") ); + if (type==STRING) + string=str; + else symbol=str; +} + +GLObject::GLObject(const char * xname, const GPList<GLObject> & xlist) : + type(LIST), name(xname), list(xlist) {} + +void +GLObject::print(ByteStream & str, int compact, int indent, int * cur_pos) const +{ + int local_cur_pos = 0; + if (!cur_pos) { cur_pos = &local_cur_pos; } + + GUTF8String buffer; + const char * to_print=0; + switch(type) + { + case NUMBER: + to_print=buffer.format("%d",number); + break; + case STRING: + { + int length = string.length(); + const char *data = (const char*)string; + buffer = GUTF8String("\""); + while (*data && length>0) + { + int span = 0; + while (span<length && (unsigned char)(data[span])>=0x20 && + data[span]!=0x7f && data[span]!='"' && data[span]!='\\' ) + span++; + if (span > 0) + { + buffer = buffer + GUTF8String(data, span); + data += span; + length -= span; + } + else + { + char buf[8]; + static char *tr1 = "\"\\tnrbf"; + static char *tr2 = "\"\\\t\n\r\b\f"; + sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[span])); + for (int i=0; tr2[i]; i++) + if (data[span] == tr2[i]) + buf[1] = tr1[i]; + if (buf[1]<'0' || buf[1]>'3') + buf[2] = 0; + buffer = buffer + GUTF8String(buf); + data += 1; + length -= 1; + } + } + buffer = buffer + GUTF8String("\""); + to_print = buffer; + } + break; + case SYMBOL: + to_print=buffer.format("%s",(const char *)symbol); + break; + case LIST: + to_print=buffer.format("(%s",(const char *)name); + break; + case INVALID: + break; + } + if (!compact && *cur_pos+strlen(to_print)>70) + { + char ch='\n'; + str.write(&ch, 1); + ch=' '; + for(int i=0;i<indent;i++) str.write(&ch, 1); + *cur_pos=indent; + } + str.write(to_print, strlen(to_print)); + char ch=' '; + str.write(&ch, 1); + *cur_pos+=strlen(to_print)+1; + if (type==LIST) + { + int indent=*cur_pos-strlen(to_print); + for(GPosition pos=list;pos;++pos) + list[pos]->print(str, compact, indent, cur_pos); + str.write(") ", 2); + *cur_pos+=2; + } +} + +// This function constructs message names for external lookup. +// The message names are constructed to avoid the problems of concatenating +// phrases (which does not translate well into other languages). The +// message names that can be generated are (listed here to appease the +// auditing program which reads comments): +// ERR_MSG("DjVuAnno.invalid2number"), ERR_MSG("DjVuAnno.string2number"), +// ERR_MSG("DjVuAnno.symbol2number"), ERR_MSG("DjVuAnno.list2number") +// ERR_MSG("DjVuAnno.invalid2string"), ERR_MSG("DjVuAnno.number2string"), +// ERR_MSG("DjVuAnno.symbol2string"), ERR_MSG("DjVuAnno.list2string") +// ERR_MSG("DjVuAnno.invalid2symbol"), ERR_MSG("DjVuAnno.number2symbol"), +// ERR_MSG("DjVuAnno.string2symbol"), ERR_MSG("DjVuAnno.list2symbol") +// ERR_MSG("DjVuAnno.invalid2list"), ERR_MSG("DjVuAnno.number2list"), +// ERR_MSG("DjVuAnno.string2list"), ERR_MSG("DjVuAnno.symbol2list") +void +GLObject::throw_can_not_convert_to(const GLObjectType to) const +{ + static const GUTF8String two('2'); + static const GUTF8String tab('\t'); + GUTF8String mesg("DjVuAnno."); + switch(type) + { + case NUMBER: + mesg+=GLObjectString[NUMBER]+two+GLObjectString[to]+tab+GUTF8String(number); + break; + case STRING: + mesg+=GLObjectString[STRING]+two+GLObjectString[to]+tab+string; + break; + case SYMBOL: + mesg+=GLObjectString[SYMBOL]+two+GLObjectString[to]+tab+symbol; + break; + case LIST: + mesg+=GLObjectString[LIST]+two+GLObjectString[to]+tab+name; + break; + default: + mesg+=GLObjectString[INVALID]+two+GLObjectString[to]; + break; + } + G_THROW(mesg); +} + +GUTF8String +GLObject::get_string(void) const +{ + if (type!=STRING) + { + throw_can_not_convert_to(STRING); + } + return string; +} + +GUTF8String +GLObject::get_symbol(void) const +{ + if (type!=SYMBOL) + { + throw_can_not_convert_to(SYMBOL); + } + return symbol; +} + +int +GLObject::get_number(void) const +{ + if (type!=NUMBER) + { + throw_can_not_convert_to(NUMBER); + } + return number; +} + +GUTF8String +GLObject::get_name(void) const +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + return name; +} + +GP<GLObject> +GLObject::operator[](int n) const +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + if (n>=list.size()) G_THROW( ERR_MSG("DjVuAnno.too_few") "\t"+name); + int i; + GPosition pos; + for(i=0, pos=list;i<n && pos;i++, ++pos) + continue; + return list[pos]; +} + +GPList<GLObject> & +GLObject::get_list(void) +{ + if (type!=LIST) + { + throw_can_not_convert_to(LIST); + } + return list; +} + +//********************************** GLParser ********************************* + +void +GLParser::skip_white_space(const char * & start) +{ + while(*start && isspace(*start)) start++; + if (!*start) + G_THROW( ByteStream::EndOfFile ); +} + +GLToken +GLParser::get_token(const char * & start) +{ + skip_white_space(start); + char c = *start; + if (c == '(') + { + start++; + return GLToken(GLToken::OPEN_PAR, 0); + } + else if (c == ')') + { + start++; + return GLToken(GLToken::CLOSE_PAR, 0); + } + else if (c=='-' || (c>='0' && c<='9')) + { + return GLToken(GLToken::OBJECT, + new GLObject(strtol(start, (char **) &start, 10))); + } + else if (c=='"') + { + GUTF8String str; + start++; + while(1) + { + int span = 0; + while (start[span] && start[span]!='\\' && start[span]!='\"') + span++; + if (span > 0) + { + str = str + GUTF8String(start,span); + start += span; + } + else if (start[0]=='\"') + { + start += 1; + break; + } + else if (start[0]=='\\' && compat) + { + char c = start[1]; + if (c == '\"') + { + start += 2; + str += '\"'; + } + else + { + start += 1; + str += '\\'; + } + } + else if (start[0]=='\\' && start[1]) + { + char c = *++start; + if (c>='0' && c<='7') + { + int x = 0; + for (int i=0; i<3 && c>='0' && c<='7'; i++) + { + x = x * 8 + c - '0'; + c = *++start; + } + str += (char)(x & 0xff); + } + else + { + static char *tr1 = "tnrbfva"; + static char *tr2 = "\t\n\r\b\f\013\007"; + for (int i=0; tr1[i]; i++) + if (c == tr1[i]) + c = tr2[i]; + start += 1; + str += c; + } + } + else + { + G_THROW( ByteStream::EndOfFile ); + } + } + return GLToken(GLToken::OBJECT, + new GLObject(GLObject::STRING, str)); + } + else + { + GUTF8String str; + while(1) + { + char ch=*start++; + if (!ch) + G_THROW( ByteStream::EndOfFile ); + if (ch==')') { start--; break; } + if (isspace(ch)) break; + str+=ch; + } + return GLToken(GLToken::OBJECT, new GLObject(GLObject::SYMBOL, str)); + } +} + +void +GLParser::parse(const char * cur_name, GPList<GLObject> & list, + const char * & start) +{ + DEBUG_MSG("GLParse::parse(): Parsing contents of object '" << cur_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + while(1) + { + GLToken token=get_token(start); + if (token.type==GLToken::OPEN_PAR) + { + if (isspace(*start)) + { + GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.paren") "\t")+cur_name; + G_THROW(mesg); + } + + GLToken tok=get_token(start); + GP<GLObject> object=tok.object; // This object should be SYMBOL + // We will convert it to LIST later + if (tok.type!=GLToken::OBJECT || object->get_type()!=GLObject::SYMBOL) + { + if (tok.type==GLToken::OPEN_PAR || + tok.type==GLToken::CLOSE_PAR) + { + GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.no_paren") "\t")+cur_name; + G_THROW(mesg); + } + if (tok.type==GLToken::OBJECT) + { + GLObject::GLObjectType type=object->get_type(); + if (type==GLObject::NUMBER) + { + GUTF8String mesg( ERR_MSG("DjVuAnno.no_number") "\t"); + mesg += cur_name; + G_THROW(mesg); + } + else if (type==GLObject::STRING) + { + GUTF8String mesg( ERR_MSG("DjVuAnno.no_string") "\t"); + mesg += cur_name; + G_THROW(mesg); + } + } + } + + // OK. Get the object contents + GPList<GLObject> new_list; + G_TRY + { + parse(object->get_symbol(), new_list, start); + } + G_CATCH(exc) + { + if (exc.cmp_cause(ByteStream::EndOfFile)) + G_RETHROW; + } + G_ENDCATCH; + list.append(new GLObject(object->get_symbol(), new_list)); + continue; + } + if (token.type==GLToken::CLOSE_PAR) + return; + list.append(token.object); + } +} + +void +GLParser::check_compat(const char *s) +{ + int state = 0; + while (s && *s && !compat) + { + switch(state) + { + case 0: + if (*s == '\"') + state = '\"'; + break; + case '\"': + if (*s == '\"') + state = 0; + else if (*s == '\\') + state = '\\'; + else if ((unsigned char)(*s)<0x20 || *s==0x7f) + compat = true; + break; + case '\\': + if (!strchr("01234567tnrbfva\"\\",*s)) + compat = true; + state = '\"'; + break; + } + s += 1; + } +} + +void +GLParser::parse(const char * str) +{ + DEBUG_MSG("GLParser::parse(): parsing string contents\n"); + DEBUG_MAKE_INDENT(3); + + G_TRY + { + check_compat(str); + parse("toplevel", list, str); + } G_CATCH(exc) + { + if (exc.cmp_cause(ByteStream::EndOfFile)) + G_RETHROW; + } G_ENDCATCH; +} + +void +GLParser::print(ByteStream & str, int compact) +{ + for(GPosition pos=list;pos;++pos) + list[pos]->print(str, compact); +} + +GP<GLObject> +GLParser::get_object(const char * name, bool last) +{ + GP<GLObject> object; + for(GPosition pos=list;pos;++pos) + { + GP<GLObject> obj=list[pos]; + if (obj->get_type()==GLObject::LIST && + obj->get_name()==name) + { + object=obj; + if (!last) break; + } + } + return object; +} + +//*************************************************************************** +//********************************** ANT ************************************ +//*************************************************************************** + +static const char *zoom_strings[]={ + "default","page","width","one2one","stretch"}; +static const int zoom_strings_size=sizeof(zoom_strings)/sizeof(const char *); + +static const char *mode_strings[]={ + "default","color","fore","back","bw"}; +static const int mode_strings_size=sizeof(mode_strings)/sizeof(const char *); + +static const char *align_strings[]={ + "default","left","center","right","top","bottom"}; +static const int align_strings_size=sizeof(align_strings)/sizeof(const char *); + +#define PNOTE_TAG "pnote" +#define BACKGROUND_TAG "background" +#define ZOOM_TAG "zoom" +#define MODE_TAG "mode" +#define ALIGN_TAG "align" +#define HALIGN_TAG "halign" +#define VALIGN_TAG "valign" +#define METADATA_TAG "metadata" + +static const unsigned long default_bg_color=0xffffffff; + +DjVuANT::DjVuANT(void) +{ + bg_color=default_bg_color; + zoom=0; + mode=MODE_UNSPEC; + hor_align=ver_align=ALIGN_UNSPEC; +} + +DjVuANT::~DjVuANT() +{ +} + +GUTF8String +DjVuANT::get_paramtags(void) const +{ + GUTF8String retval; + if(zoom > 0) + { + retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom)+="\" />\n"; + }else if(zoom && ((-zoom)<zoom_strings_size)) + { + retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom_strings[-zoom])+"\" />\n"; + } + if((mode>0)&&(mode<mode_strings_size)) + { + retval+="<PARAM name=\"" MODE_TAG "\" value=\""+GUTF8String(mode_strings[mode])+"\" />\n"; + } + if((hor_align>ALIGN_UNSPEC)&&(hor_align<align_strings_size)) + { + retval+="<PARAM name=\"" HALIGN_TAG "\" value=\""+GUTF8String(align_strings[hor_align])+"\" />\n"; + } + if((ver_align>ALIGN_UNSPEC)&&(ver_align<align_strings_size)) + { + retval+="<PARAM name=\"" VALIGN_TAG "\" value=\""+GUTF8String(align_strings[ver_align])+"\" />\n"; + } + if((bg_color&0xffffff) == bg_color) + { + retval+="<PARAM name=\"" BACKGROUND_TAG "\" value=\""+GUTF8String().format("#%06lX",bg_color)+"\" />\n"; + } + return retval; +} + +void +DjVuANT::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + +GUTF8String +DjVuANT::get_xmlmap(const GUTF8String &name,const int height) const +{ + GUTF8String retval("<MAP name=\""+name.toEscaped()+"\" >\n"); + for(GPosition pos(map_areas);pos;++pos) + { + retval+=map_areas[pos]->get_xmltag(height); + } + return retval+"</MAP>\n"; +} + +void +DjVuANT::writeMap( + ByteStream &str_out,const GUTF8String &name,const int height) const +{ + str_out.writestring("<MAP name=\""+name.toEscaped()+"\" >\n"); + for(GPosition pos(map_areas);pos;++pos) + { + str_out.writestring(GUTF8String(map_areas[pos]->get_xmltag(height))); + } + str_out.writestring(GUTF8String("</MAP>\n")); +} + +GUTF8String +DjVuANT::read_raw(ByteStream & str) +{ + GUTF8String raw; + char buffer[1024]; + int length; + while((length=str.read(buffer, 1024))) + raw+=GUTF8String(buffer, length); + return raw; +} + +void +DjVuANT::decode(class GLParser & parser) +{ + bg_color=get_bg_color(parser); + zoom=get_zoom(parser); + mode=get_mode(parser); + hor_align=get_hor_align(parser); + ver_align=get_ver_align(parser); + map_areas=get_map_areas(parser); +#ifndef NO_METADATA_IN_ANT_CHUNK + metadata=get_metadata(parser); +#endif +} + + +void +DjVuANT::decode(ByteStream & str) +{ + GLParser parser(read_raw(str)); + decode(parser); +} + +void +DjVuANT::merge(ByteStream & str) +{ + GLParser parser(encode_raw()); + GUTF8String add_raw=read_raw(str); + parser.parse(add_raw); + decode(parser); +} + +void +DjVuANT::encode(ByteStream &bs) +{ + GUTF8String raw=encode_raw(); + bs.writall((const char*) raw, raw.length()); +} + +unsigned int +DjVuANT::get_memory_usage() const +{ + return sizeof(DjVuANT); +} + +unsigned char +DjVuANT::decode_comp(char ch1, char ch2) +{ + unsigned char dig1=0; + if (ch1) + { + ch1=toupper(ch1); + if (ch1>='0' && ch1<='9') dig1=ch1-'0'; + if (ch1>='A' && ch1<='F') dig1=10+ch1-'A'; + + unsigned char dig2=0; + if (ch2) + { + ch2=toupper(ch2); + if (ch2>='0' && ch2<='9') dig2=ch2-'0'; + if (ch2>='A' && ch2<='F') dig2=10+ch2-'A'; + return (dig1 << 4) | dig2; + } + return dig1; + } + return 0; +} + +unsigned long int +DjVuANT::cvt_color(const char * color, unsigned long int def) +{ + if (color[0]!='#') return def; + + unsigned long int color_rgb=0; + color++; + const char * start, * end; + + // Do blue + end=color+strlen(color); start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0); + + // Do green + end=color+strlen(color)-2; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 8; + + // Do red + end=color+strlen(color)-4; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 16; + + // Do the fourth byte + end=color+strlen(color)-6; start=end-2; + if (start<color) start=color; + if (end>start) + color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 24; + + return color_rgb; +} + +unsigned long int +DjVuANT::get_bg_color(GLParser & parser) +{ + unsigned long retval=default_bg_color; + DEBUG_MSG("DjVuANT::get_bg_color(): getting background color ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(BACKGROUND_TAG); + if (obj && obj->get_list().size()==1) + { + GUTF8String color=(*obj)[0]->get_symbol(); + DEBUG_MSG("color='" << color << "'\n"); + retval=cvt_color(color, 0xffffff); + } +#ifndef NDEBUG + if(retval == default_bg_color) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == default_bg_color) + { + DEBUG_MSG("resetting color to 0xffffffff (UNSPEC)\n"); + } +#endif // NDEBUG + return retval; +} + +int +DjVuANT::get_zoom(GLParser & parser) + // Returns: + // <0 - special zoom (like ZOOM_STRETCH) + // =0 - not set + // >0 - numeric zoom (%%) +{ + int retval=ZOOM_UNSPEC; + DEBUG_MSG("DjVuANT::get_zoom(): getting zoom factor ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(ZOOM_TAG); + if (obj && obj->get_list().size()==1) + { + const GUTF8String zoom((*obj)[0]->get_symbol()); + DEBUG_MSG("zoom='" << zoom << "'\n"); + + for(int i=0;(i<zoom_strings_size);++i) + { + if(zoom == zoom_strings[i]) + { + retval=(-i); + break; + } + } + if(retval == ZOOM_UNSPEC) + { + if (zoom[0]!='d') + { + G_THROW( ERR_MSG("DjVuAnno.bad_zoom") ); + }else + { + retval=zoom.substr(1, zoom.length()).toInt(); //atoi((const char *) zoom+1); + } + } + } +#ifndef NDEBUG + if(retval == ZOOM_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ZOOM_UNSPEC) + { + DEBUG_MSG("resetting zoom to 0 (UNSPEC)\n"); + } +#endif // NDEBUG + return retval; +} + +int +DjVuANT::get_mode(GLParser & parser) +{ + int retval=MODE_UNSPEC; + DEBUG_MSG("DjVuAnt::get_mode(): getting default mode ...\n"); + DEBUG_MAKE_INDENT(3); + G_TRY + { + GP<GLObject> obj=parser.get_object(MODE_TAG); + if (obj && obj->get_list().size()==1) + { + const GUTF8String mode((*obj)[0]->get_symbol()); + DEBUG_MSG("mode='" << mode << "'\n"); + for(int i=0;(i<mode_strings_size);++i) + { + if(mode == mode_strings[i]) + { + retval=i; + break; + } + } + } +#ifndef NDEBUG + if(retval == MODE_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == MODE_UNSPEC) + { + DEBUG_MSG("resetting mode to MODE_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +static inline DjVuANT::alignment +legal_halign(const int i) +{ + DjVuANT::alignment retval; + switch((DjVuANT::alignment)i) + { + case DjVuANT::ALIGN_LEFT: + case DjVuANT::ALIGN_CENTER: + case DjVuANT::ALIGN_RIGHT: + retval=(DjVuANT::alignment)i; + break; + default: + retval=DjVuANT::ALIGN_UNSPEC; + break; + } + return retval; +} + +static inline DjVuANT::alignment +legal_valign(const int i) +{ + DjVuANT::alignment retval; + switch((DjVuANT::alignment)i) + { + case DjVuANT::ALIGN_CENTER: + case DjVuANT::ALIGN_TOP: + case DjVuANT::ALIGN_BOTTOM: + retval=(DjVuANT::alignment)i; + break; + default: + retval=DjVuANT::ALIGN_UNSPEC; + break; + } + return retval; +} + +DjVuANT::alignment +DjVuANT::get_hor_align(GLParser & parser) +{ + DEBUG_MSG("DjVuAnt::get_hor_align(): getting hor page alignemnt ...\n"); + DEBUG_MAKE_INDENT(3); + alignment retval=ALIGN_UNSPEC; + G_TRY + { + GP<GLObject> obj=parser.get_object(ALIGN_TAG); + if (obj && obj->get_list().size()==2) + { + const GUTF8String align((*obj)[0]->get_symbol()); + DEBUG_MSG("hor_align='" << align << "'\n"); + + for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i) + { + const alignment j=legal_halign(i); + if((i == (int)j)&&(align == align_strings[i])) + { + retval=j; + break; + } + } + } +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +DjVuANT::alignment +DjVuANT::get_ver_align(GLParser & parser) +{ + DEBUG_MSG("DjVuAnt::get_ver_align(): getting vert page alignemnt ...\n"); + DEBUG_MAKE_INDENT(3); + alignment retval=ALIGN_UNSPEC; + G_TRY + { + GP<GLObject> obj=parser.get_object(ALIGN_TAG); + if (obj && obj->get_list().size()==2) + { + const GUTF8String align((*obj)[1]->get_symbol()); + DEBUG_MSG("ver_align='" << align << "'\n"); + for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i) + { + const alignment j=legal_valign(i); + if((i == (int)j)&&(align == align_strings[i])) + { + retval=j; + break; + } + } + } +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("can't find any.\n"); + } +#endif // NDEBUG + } G_CATCH_ALL {} G_ENDCATCH; +#ifndef NDEBUG + if(retval == ALIGN_UNSPEC) + { + DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n"); + } +#endif // NDEBUG + return retval; +} + +#ifndef NO_METADATA_IN_ANT_CHUNK +GMap<GUTF8String, GUTF8String> +DjVuANT::get_metadata(GLParser & parser) +{ + DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n"); + DEBUG_MAKE_INDENT(3); + + GMap<GUTF8String, GUTF8String> mdata; + + GPList<GLObject> list=parser.get_list(); + for(GPosition pos=list;pos;++pos) + { + GLObject & obj=*list[pos]; + if (obj.get_type()==GLObject::LIST && obj.get_name()==METADATA_TAG) + { + G_TRY + { + for(int obj_num=0;obj_num<obj.get_list().size();obj_num++) + { + GLObject & el=*obj[obj_num]; + const int type = el.get_type(); + if (type == GLObject::LIST) + { + const GUTF8String & name=el.get_name(); + mdata[name]=(el[0])->get_string(); + } + } + } + G_CATCH_ALL { } G_ENDCATCH; + } + } + return mdata; +} +#endif + +GPList<GMapArea> +DjVuANT::get_map_areas(GLParser & parser) +{ + DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n"); + DEBUG_MAKE_INDENT(3); + + GPList<GMapArea> map_areas; + + GPList<GLObject> list=parser.get_list(); + + for(GPosition pos=list;pos;++pos) + { + GLObject & obj=*list[pos]; + const int type=obj.get_type(); + if (type == GLObject::LIST) + { + const GUTF8String name=obj.get_name(); + if(name == GMapArea::MAPAREA_TAG) + { + G_TRY { + // Getting the url + GUTF8String url; + GUTF8String target=GMapArea::TARGET_SELF; + GLObject & url_obj=*(obj[0]); + if (url_obj.get_type()==GLObject::LIST) + { + if (url_obj.get_name()!=GMapArea::URL_TAG) + G_THROW( ERR_MSG("DjVuAnno.bad_url") ); + url=(url_obj[0])->get_string(); + target=(url_obj[1])->get_string(); + } else url=url_obj.get_string(); + + // Getting the comment + GUTF8String comment=(obj[1])->get_string(); + + DEBUG_MSG("found maparea '" << comment << "' (" << + url << ":" << target << ")\n"); + + GLObject * shape=obj[2]; + GP<GMapArea> map_area; + if (shape->get_type()==GLObject::LIST) + { + if (shape->get_name()==GMapArea::RECT_TAG) + { + DEBUG_MSG("it's a rectangle.\n"); + GRect grect((*shape)[0]->get_number(), + (*shape)[1]->get_number(), + (*shape)[2]->get_number(), + (*shape)[3]->get_number()); + GP<GMapRect> map_rect=GMapRect::create(grect); + map_area=(GMapRect *)map_rect; + } else if (shape->get_name()==GMapArea::POLY_TAG) + { + DEBUG_MSG("it's a polygon.\n"); + int points=shape->get_list().size()/2; + GTArray<int> xx(points-1), yy(points-1); + for(int i=0;i<points;i++) + { + xx[i]=(*shape)[2*i]->get_number(); + yy[i]=(*shape)[2*i+1]->get_number(); + } + GP<GMapPoly> map_poly=GMapPoly::create(xx,yy,points); + map_area=(GMapPoly *)map_poly; + } else if (shape->get_name()==GMapArea::OVAL_TAG) + { + DEBUG_MSG("it's an ellipse.\n"); + GRect grect((*shape)[0]->get_number(), + (*shape)[1]->get_number(), + (*shape)[2]->get_number(), + (*shape)[3]->get_number()); + GP<GMapOval> map_oval=GMapOval::create(grect); + map_area=(GMapOval *)map_oval; + } + } + + if (map_area) + { + map_area->url=url; + map_area->target=target; + map_area->comment=comment; + for(int obj_num=3;obj_num<obj.get_list().size();obj_num++) + { + GLObject * el=obj[obj_num]; + if (el->get_type()==GLObject::LIST) + { + const GUTF8String & name=el->get_name(); + if (name==GMapArea::BORDER_AVIS_TAG) + map_area->border_always_visible=true; + else if (name==GMapArea::HILITE_TAG) + { + GLObject * obj=el->get_list()[el->get_list().firstpos()]; + if (obj->get_type()==GLObject::SYMBOL) + map_area->hilite_color=cvt_color(obj->get_symbol(), 0xff); + } else + { + int border_type= + name==GMapArea::NO_BORDER_TAG ? GMapArea::NO_BORDER : + name==GMapArea::XOR_BORDER_TAG ? GMapArea::XOR_BORDER : + name==GMapArea::SOLID_BORDER_TAG ? GMapArea::SOLID_BORDER : + name==GMapArea::SHADOW_IN_BORDER_TAG ? GMapArea::SHADOW_IN_BORDER : + name==GMapArea::SHADOW_OUT_BORDER_TAG ? GMapArea::SHADOW_OUT_BORDER : + name==GMapArea::SHADOW_EIN_BORDER_TAG ? GMapArea::SHADOW_EIN_BORDER : + name==GMapArea::SHADOW_EOUT_BORDER_TAG ? GMapArea::SHADOW_EOUT_BORDER : -1; + if (border_type>=0) + { + map_area->border_type=(GMapArea::BorderType) border_type; + for(GPosition pos=el->get_list();pos;++pos) + { + GLObject * obj=el->get_list()[pos]; + if (obj->get_type()==GLObject::SYMBOL) + map_area->border_color=cvt_color(obj->get_symbol(), 0xff); + if (obj->get_type()==GLObject::NUMBER) + map_area->border_width=obj->get_number(); + } + } + } + } // if (el->get_type()==...) + } // for(int obj_num=...) + map_areas.append(map_area); + } // if (map_area) ... + } G_CATCH_ALL {} G_ENDCATCH; + } + } + } // while(item==...) + + DEBUG_MSG("map area list size = " << list.size() << "\n"); + + return map_areas; +} + +void +DjVuANT::del_all_items(const char * name, GLParser & parser) +{ + GPList<GLObject> & list=parser.get_list(); + GPosition pos=list; + while(pos) + { + GLObject & obj=*list[pos]; + if (obj.get_type()==GLObject::LIST && + obj.get_name()==name) + { + GPosition this_pos=pos; + ++pos; + list.del(this_pos); + } else ++pos; + } +} + +GUTF8String +DjVuANT::encode_raw(void) const +{ + GUTF8String buffer; + GLParser parser; + + //*** Background color + del_all_items(BACKGROUND_TAG, parser); + if (bg_color!=default_bg_color) + { + buffer.format("(" BACKGROUND_TAG " #%02X%02X%02X)", + (unsigned int)((bg_color & 0xff0000) >> 16), + (unsigned int)((bg_color & 0xff00) >> 8), + (unsigned int)(bg_color & 0xff)); + parser.parse(buffer); + } + + //*** Zoom + del_all_items(ZOOM_TAG, parser); + if (zoom!=ZOOM_UNSPEC) + { + buffer="(" ZOOM_TAG " "; + const int i=1-zoom; + if((i>=0)&& (i<zoom_strings_size)) + { + buffer+=zoom_strings[i]; + }else + { + buffer+="d"+GUTF8String(zoom); + } + buffer+=")"; + parser.parse(buffer); + } + + //*** Mode + del_all_items(MODE_TAG, parser); + if (mode!=MODE_UNSPEC) + { + const int i=mode-1; + if((i>=0)&& (i<mode_strings_size)) + { + buffer="(" MODE_TAG " " + GUTF8String(mode_strings[mode]) + ")"; + } + parser.parse(buffer); + } + + //*** Alignment + del_all_items(ALIGN_TAG, parser); + if (hor_align!=ALIGN_UNSPEC || ver_align!=ALIGN_UNSPEC) + { + buffer= GUTF8String("(" ALIGN_TAG " ") + +align_strings[((hor_align<ALIGN_UNSPEC)|| + (hor_align>=align_strings_size))?ALIGN_UNSPEC:hor_align] + +" "+align_strings[((ver_align<ALIGN_UNSPEC)|| + (ver_align>=align_strings_size))?ALIGN_UNSPEC:ver_align]+")"; + parser.parse(buffer); + } + //*** Metadata +#ifndef NO_METADATA_IN_ANT_CHUNK + del_all_items(METADATA_TAG, parser); + if (!metadata.isempty()) + { + GUTF8String mdatabuffer("("); + mdatabuffer += METADATA_TAG ; + for (GPosition pos=metadata; pos; ++pos) + mdatabuffer +=" (" + metadata.key(pos)+" \""+metadata[pos]+"\")"; + mdatabuffer += " )"; + parser.parse(mdatabuffer); + } +#endif + //*** Mapareas + del_all_items(GMapArea::MAPAREA_TAG, parser); + for(GPosition pos=map_areas;pos;++pos) + parser.parse(map_areas[pos]->print()); + + GP<ByteStream> gstr=ByteStream::create(); + ByteStream &str=*gstr; + parser.print(str, 1); + GUTF8String ans; + int size = str.size(); + str.seek(0); + str.read(ans.getbuf(size), size); + return ans; +} + +bool +DjVuANT::is_empty(void) const +{ + GUTF8String raw=encode_raw(); + for(int i=raw.length()-1;i>=0;i--) + if (isspace(raw[i])) raw.setat(i, 0); + else break; + return raw.length()==0; +} + +GP<DjVuANT> +DjVuANT::copy(void) const +{ + GP<DjVuANT> ant=new DjVuANT(*this); + + + // Now process the list of hyperlinks. + ant->map_areas.empty(); + for(GPosition pos=map_areas;pos;++pos) + ant->map_areas.append(map_areas[pos]->get_copy()); + + return ant; +} + +//*************************************************************************** +//******************************** DjVuAnno ********************************* +//*************************************************************************** + +GUTF8String +DjVuAnno::get_xmlmap(const GUTF8String &name,const int height) const +{ + return ant + ?(ant->get_xmlmap(name,height)) + :("<MAP name=\""+name.toEscaped()+"\"/>\n"); +} + +void +DjVuAnno::writeMap(ByteStream &str_out,const GUTF8String &name,const int height) const +{ + if(ant) + { + ant->writeMap(str_out,name,height); + }else + { + str_out.writestring(get_xmlmap(name,height)); + } +} + +GUTF8String +DjVuAnno::get_paramtags(void) const +{ + return ant + ?(ant->get_paramtags()) + :GUTF8String(); +} + +void +DjVuAnno::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + + +void +DjVuAnno::decode(const GP<ByteStream> &gbs) +{ + GUTF8String chkid; + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + while( iff.get_chunk(chkid) ) + { + if (chkid == "ANTa") + { + if (ant) { + ant->merge(*iff.get_bytestream()); + } else { + ant=DjVuANT::create(); + ant->decode(*iff.get_bytestream()); + } + } + else if (chkid == "ANTz") + { + GP<ByteStream> gbsiff=BSByteStream::create(giff->get_bytestream()); + if (ant) { + ant->merge(*gbsiff); + } else { + ant=DjVuANT::create(); + ant->decode(*gbsiff); + } + } + // Add decoding of other chunks here + iff.close_chunk(); + } +} + +void +DjVuAnno::encode(const GP<ByteStream> &gbs) +{ + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + if (ant) + { +#if 0 + iff.put_chunk("ANTa"); + ant->encode(iff); + iff.close_chunk(); +#else + iff.put_chunk("ANTz"); + { +// GP<ByteStream> bsbinput = giff.get_bytestream(); + GP<ByteStream> bsb = BSByteStream::create(giff->get_bytestream(), 50); + ant->encode(*bsb); + } + iff.close_chunk(); +#endif + } + // Add encoding of other chunks here +} + + +GP<DjVuAnno> +DjVuAnno::copy(void) const +{ + GP<DjVuAnno> anno= new DjVuAnno; + // Copy any primitives (if any) + *anno=*this; + // Copy each substructure + if (ant) anno->ant = ant->copy(); + return anno; +} + +void +DjVuAnno::merge(const GP<DjVuAnno> & anno) +{ + if (anno) + { + GP<ByteStream> gstr=ByteStream::create(); + encode(gstr); + anno->encode(gstr); + gstr->seek(0); + decode(gstr); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h new file mode 100644 index 00000000..964c6c44 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.h @@ -0,0 +1,295 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuAnno.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUANNO_H +#define _DJVUANNO_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + + +/** @name DjVuAnno.h + + Files #"DjVuAnno.h"# and #"DjVuAnno.cpp"# implement the mechanism for + annotating DjVuImages. Annotations are additional instructions for the + plugin about how the image should be displayed. The exact format of + annotations is not strictly defined. The only requirement is that they + have to be stored as a sequence of chunks inside a #FORM:ANNO#. + + This file implements annotations understood by the DjVu plugins + and encoders. + + + using: contents of #ANT*# chunks. + + Contents of the #FORM:ANNO# should be passed to \Ref{DjVuAnno::decode}() + for parsing, which initializes \Ref{DjVuAnno::ANT} + and fills them with decoded data. + @memo Implements support for DjVuImage annotations + @author Andrei Erofeev <[email protected]> + @version + #$Id: DjVuAnno.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GMapArea; +class ByteStream; + +// -------- DJVUANT -------- + +/** This class contains some trivial annotations of the page or of the + document such as page border color, page alignment, initial zoom and + display mode, hyperlinks and highlighted areas. All this information is + put inside a textual chunk #ANTa# in pseudo-lisp format. Decoding and + encoding are normally done by \Ref{DjVuANT::decode}() and + \Ref{DjVuANT::encode}() functions. */ + +class DjVuANT : public GPEnabled +{ +protected: + /// Constructs an empty annotation object. + DjVuANT(void); + +public: + enum { MODE_UNSPEC=0, MODE_COLOR, MODE_FORE, MODE_BACK, MODE_BW }; + enum { ZOOM_STRETCH=-4, ZOOM_ONE2ONE=-3, ZOOM_WIDTH=-2, + ZOOM_PAGE=-1, ZOOM_UNSPEC=0 }; + enum alignment { ALIGN_UNSPEC=0, ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, + ALIGN_TOP, ALIGN_BOTTOM }; + + /// Creates an empty annotation object. + static GP<DjVuANT> create(void) { return new DjVuANT; } + virtual ~DjVuANT(); + + /** Background color. Is in #0x00RRBBGG# format. #0xffffffff# if + there were no background color records in the annotation chunk. */ + unsigned long int bg_color; + /** Initial zoom. Possible values are: + \begin{description} + \item[ZOOM_STRETCH] the image is stretched to the viewport. + \item[ZOOM_ONE2ONE] the image is displayed pixel-to-pixel. + \item[ZOOM_WIDTH] "Fit width" mode. + \item[ZOOM_PAGE] "Fit page" mode. + \item[ZOOM_UNSPEC] Annotation does not specify a zoom factor. + \end{description} */ + int zoom; + /** Initial mode. Possible values are: + \begin{description} + \item[MODE_COLOR] color mode. + \item[MODE_FORE] foreground mode. + \item[MODE_BACK] background mode. + \item[MODE_BW] black and white mode. + \item[MODE_UNSPEC] Annotation does not specify a display mode. + \item[Any positive number] Zoom in \%. Please note that + all constants above are either negative or ZERO. Thus + it's possible to distinguish numerical zoom from those + special cases. + \end{description} */ + int mode; + /** Horizontal page alignment. Possible values are #ALIGN_LEFT#, + #ALIGN_CENTER#, #ALIGN_RIGHT# and #ALIGN_UNSPEC#. */ + alignment hor_align; + /** Vertical page alignment. Possible values are #ALIGN_TOP#, + #ALIGN_CENTER#, #ALIGN_BOTTOM# and #ALIGN_UNSPEC#. */ + alignment ver_align; + /** List of defined map areas. They may be just areas of highlighting + or hyperlink. Please refer to \Ref{GMapArea}, \Ref{GMapRect}, + \Ref{GMapPoly} and \Ref{GMapOval} for details. */ + GPList<GMapArea> map_areas; +#ifndef NO_METADATA_IN_ANT_CHUNK + /** Metainformations like title, author ... */ + GMap<GUTF8String,GUTF8String> metadata; +#endif + /** Returns TRUE if no features are specified or specified features + are not different from default ones */ + bool is_empty(void) const; + + /** Decodes contents of annotation chunk #ANTa#. The chunk data is + read from ByteStream #bs# until reaching an end-of-stream marker. + This function is normally called after a call to + \Ref{IFFByteStream::get_chunk}(). */ + void decode(ByteStream &bs); + + /** Same as \Ref{decode}() but adds the new data to what has + been decoded before. */ + void merge(ByteStream & bs); + + /** Encodes the #ANTa# chunk. The annotation data is simply written + into ByteStream #bs# with no IFF header. This function is normally + called after a call to \Ref{IFFByteStream::put_chunk}(). */ + void encode(ByteStream &bs); + + /// Encodes data back into raw annotation data. + GUTF8String encode_raw(void) const; + + /// Returns a copy of this object + GP<DjVuANT> copy(void) const; + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + unsigned int get_memory_usage() const; + + /// Converts color from string in \#RRGGBB notation to an unsigned integer + static unsigned long int cvt_color(const char * color, unsigned long int def); + /// Obtain the <MAP></MAP> tag for these annotations. + GUTF8String get_xmlmap(const GUTF8String &name, const int height) const; + /// Write the <MAP></MAP> tag for these annotations. + void writeMap( + ByteStream &bs,const GUTF8String &name, const int height) const; + /// Obtain the XML flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Write the XML flags for the default specifications. + void writeParam(ByteStream &out_str) const; +private: + void decode(class GLParser & parser); + static GUTF8String read_raw(ByteStream & str); + static unsigned char decode_comp(char ch1, char ch2); + static unsigned long int get_bg_color(class GLParser & parser); + static int get_zoom(class GLParser & parser); + static int get_mode(class GLParser & parser); + static alignment get_hor_align(class GLParser & parser); + static alignment get_ver_align(class GLParser & parser); + static GPList<GMapArea> get_map_areas(class GLParser & parser); +#ifndef NO_METADATA_IN_ANT_CHUNK + static GMap<GUTF8String, GUTF8String>get_metadata(GLParser & parser); +#endif + static void del_all_items(const char * name, class GLParser & parser); +}; + +// -------- DJVUANNO -------- + + +/** This is a top-level class containing annotations of a DjVu document (or + just a page). It has only two functions: \Ref{encode}() and + \Ref{decode}(). Both of them work with a sequence of annotation chunks + from #FORM:ANNO# form. Basing on the name of the chunks they call + #encode()# and #decode()# functions of the proper annotation structure + (like \Ref{ANT}). The real work of encoding and decoding is done by + lower-level classes. */ +class DjVuAnno : public GPEnabled +{ +protected: + DjVuAnno(void) {} +public: + /// Creation method. + static GP<DjVuAnno> create(void) { return new DjVuAnno; } + + GP<DjVuANT> ant; + + /** Decodes a sequence of annotation chunks and merges contents of every + chunk with previously decoded information. This function + should be called right after applying \Ref{IFFByteStream::get_chunk}() + to data from #FORM:ANNO#. */ + void decode(const GP<ByteStream> &bs); + + /** Encodes all annotations back into a sequence of chunks to be put + inside a #FORM:ANNO#. */ + void encode(const GP<ByteStream> &bs); + + /// Returns a copy of this object + GP<DjVuAnno> copy(void) const; + + /** Merged the contents of this class and of annotations + pointed by #anno# pointer */ + void merge(const GP<DjVuAnno> & anno); + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + inline unsigned int get_memory_usage() const; + /// Obtain the <MAP></MAP> tag for these annotations. + GUTF8String get_xmlmap(const GUTF8String &name, const int height) const; + /// Write the <MAP></MAP> tag for these annotations. + void writeMap( + ByteStream &bs,const GUTF8String &name, const int height) const; + /// Obtain the XML flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Write the XML flags for the default specifications. + void writeParam(ByteStream &out_str) const; +private: // dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +//@} + +inline unsigned int +DjVuAnno::get_memory_usage() const +{ + return (ant)?(ant->get_memory_usage()):0; +} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp new file mode 100644 index 00000000..542faa7a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp @@ -0,0 +1,2193 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocEditor.cpp,v 1.13 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDocEditor.h" +#include "DjVuImage.h" +#include "IFFByteStream.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "GOS.h" +#include "GURL.h" +#include "DjVuAnno.h" +#include "GRect.h" +#include "DjVmNav.h" + +#include "debug.h" + +#include <ctype.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char octets[4]={0x41,0x54,0x26,0x54}; + +int DjVuDocEditor::thumbnails_per_file=10; + +// This is a structure for active files and DataPools. It may contain +// a DjVuFile, which is currently being used by someone (I check the list +// and get rid of hanging files from time to time) or a DataPool, +// which is "custom" with respect to the document (was modified or +// inserted), or both. +// +// DjVuFile is set to smth!=0 when it's created using url_to_file(). +// It's reset back to ZERO in clean_files_map() when +// it sees, that a given file is not used by anyone. +// DataPool is updated when a file is inserted +class DjVuDocEditor::File : public GPEnabled +{ +public: + // 'pool' below may be non-zero only if it cannot be retrieved + // by the DjVuDocument, that is it either corresponds to a + // modified DjVuFile or it has been inserted. Otherwise it's ZERO + // Once someone assigns a non-zero DataPool, it remains non-ZERO + // (may be updated if the file gets modified) and may be reset + // only by save() or save_as() functions. + GP<DataPool> pool; + + // If 'file' is non-zero, it means, that it's being used by someone + // We check for unused files from time to time and ZERO them. + // But before we do it, we may save the DataPool in the case if + // file has been modified. + GP<DjVuFile> file; +}; + +void +DjVuDocEditor::check(void) +{ + if (!initialized) G_THROW( ERR_MSG("DjVuDocEditor.not_init") ); +} + +DjVuDocEditor::DjVuDocEditor(void) +{ + initialized=false; + refresh_cb=0; + refresh_cl_data=0; +} + +DjVuDocEditor::~DjVuDocEditor(void) +{ + if (!tmp_doc_url.is_empty()) + { + tmp_doc_url.deletefile(); + } + + GCriticalSectionLock lock(&thumb_lock); + thumb_map.empty(); + DataPool::close_all(); +} + +void +DjVuDocEditor::init(void) +{ + DEBUG_MSG("DjVuDocEditor::init() called\n"); + DEBUG_MAKE_INDENT(3); + + // If you remove this check be sure to delete thumb_map + if (initialized) G_THROW( ERR_MSG("DjVuDocEditor.init") ); + + doc_url=GURL::Filename::UTF8("noname.djvu"); + + const GP<DjVmDoc> doc(DjVmDoc::create()); + const GP<ByteStream> gstr(ByteStream::create()); + doc->write(gstr); + gstr->seek(0, SEEK_SET); + doc_pool=DataPool::create(gstr); + + orig_doc_type=UNKNOWN_TYPE; + orig_doc_pages=0; + + initialized=true; + + DjVuDocument::init(doc_url, this); +} + +void +DjVuDocEditor::init(const GURL &url) +{ + DEBUG_MSG("DjVuDocEditor::init() called: url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // If you remove this check be sure to delete thumb_map + if (initialized) + G_THROW( ERR_MSG("DjVuDocEditor.init") ); + + // First - create a temporary DjVuDocument and check its type + doc_pool=DataPool::create(url); + doc_url=url; + const GP<DjVuDocument> tmp_doc(DjVuDocument::create_wait(doc_url,this)); + if (!tmp_doc->is_init_ok()) + G_THROW( ERR_MSG("DjVuDocEditor.open_fail") "\t" +url.get_string()); + + orig_doc_type=tmp_doc->get_doc_type(); + orig_doc_pages=tmp_doc->get_pages_num(); + if (orig_doc_type==OLD_BUNDLED || + orig_doc_type==OLD_INDEXED || + orig_doc_type==SINGLE_PAGE) + { + // Suxx. I need to convert it NOW. + // We will unlink this file in the destructor + tmp_doc_url=GURL::Filename::Native(tmpnam(0)); + const GP<ByteStream> gstr(ByteStream::create(tmp_doc_url, "wb")); + tmp_doc->write(gstr, true); // Force DJVM format + gstr->flush(); + doc_pool=DataPool::create(tmp_doc_url); + } + + // OK. Now doc_pool contains data of the document in one of the + // new formats. It will be a lot easier to insert/delete pages now. + + // 'doc_url' below of course doesn't refer to the file with the converted + // data, but we will take care of it by redirecting the request_data(). + initialized=true; + DjVuDocument::init(doc_url, this); + + // Cool. Now extract the thumbnails... + GCriticalSectionLock lock(&thumb_lock); + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + // Call DjVuDocument::get_thumbnail() here to bypass logic + // of DjVuDocEditor::get_thumbnail(). init() is the only safe + // place where we can still call DjVuDocument::get_thumbnail(); + const GP<DataPool> pool(DjVuDocument::get_thumbnail(page_num, true)); + if (pool) + { + thumb_map[page_to_id(page_num)]=pool; + } + } + // And remove then from DjVmDir so that DjVuDocument + // does not try to use them + unfile_thumbnails(); +} + +GP<DataPool> +DjVuDocEditor::request_data(const DjVuPort * source, const GURL & url) +{ + DEBUG_MSG("DjVuDocEditor::request_data(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Check if we have either original data or converted (to new format), + // if all the story is about the DjVuDocument's data + if (url==doc_url) + return doc_pool; + + // Now see if we have any file matching the url + const GP<DjVmDir::File> frec(djvm_dir->name_to_file(url.fname())); + if (frec) + { + GCriticalSectionLock lock(&files_lock); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + const GP<File> f(files_map[pos]); + if (f->file && f->file->get_init_data_pool()) + return f->file->get_init_data_pool();// Favor DjVuFile's knowledge + else if (f->pool) return f->pool; + } + } + + // Finally let DjVuDocument cope with it. It may be a connected DataPool + // for a BUNDLED format. Or it may be a file. Anyway, it was not + // manually included, so it should be in the document. + const GP<DataPool> pool(DjVuDocument::request_data(source, url)); + + // We do NOT update the 'File' structure, because our rule is that + // we keep a separate copy of DataPool in 'File' only if it cannot + // be retrieved from DjVuDocument (like it has been "inserted" or + // corresponds to a modified file). + return pool; +} + +void +DjVuDocEditor::clean_files_map(void) + // Will go thru the map of files looking for unreferenced + // files or records w/o DjVuFile and DataPool. + // These will be modified and/or removed. +{ + DEBUG_MSG("DjVuDocEditor::clean_files_map() called\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&files_lock); + + // See if there are too old items in the "cache", which are + // not referenced by anyone. If the corresponding DjVuFile has been + // modified, obtain the new data and replace the 'pool'. Clear the + // DjVuFile anyway. If both DataPool and DjVuFile are zero, remove + // the entry. + for(GPosition pos=files_map;pos;) + { + const GP<File> f(files_map[pos]); + if (f->file && f->file->get_count()==1) + { + DEBUG_MSG("ZEROing file '" << f->file->get_url() << "'\n"); + if (f->file->is_modified()) + f->pool=f->file->get_djvu_data(false); + f->file=0; + } + if (!f->file && !f->pool) + { + DEBUG_MSG("Removing record '" << files_map.key(pos) << "'\n"); + GPosition this_pos=pos; + ++pos; + files_map.del(this_pos); + } else ++pos; + } +} + +GP<DjVuFile> +DjVuDocEditor::url_to_file(const GURL & url, bool dont_create) const +{ + DEBUG_MSG("DjVuDocEditor::url_to_file(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Check if have a DjVuFile with this url cached (created before + // and either still active or left because it has been modified) + GP<DjVmDir::File> frec; + if((const DjVmDir *)djvm_dir) + frec=djvm_dir->name_to_file(url.fname()); + if (frec) + { + GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock)); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + const GP<File> f(files_map[pos]); + if (f->file) + return f->file; + } + } + + const_cast<DjVuDocEditor *>(this)->clean_files_map(); + + // We don't have the file cached. Let DjVuDocument create the file. + const GP<DjVuFile> file(DjVuDocument::url_to_file(url, dont_create)); + + // And add it to our private "cache" + if (file && frec) + { + GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock)); + GPosition pos; + if (files_map.contains(frec->get_load_name(), pos)) + { + files_map[frec->get_load_name()]->file=file; + }else + { + const GP<File> f(new File()); + f->file=file; + const_cast<DjVuDocEditor *>(this)->files_map[frec->get_load_name()]=f; + } + } + + return file; +} + +GUTF8String +DjVuDocEditor::page_to_id(int page_num) const +{ + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num)); + const GP<DjVmDir::File> f(djvm_dir->page_to_file(page_num)); + if (! f) + G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num)); + + return f->get_load_name(); +} + +GUTF8String +DjVuDocEditor::find_unique_id(GUTF8String id) +{ + const GP<DjVmDir> dir(get_djvm_dir()); + + GUTF8String base, ext; + const int dot=id.rsearch('.'); + if(dot >= 0) + { + base=id.substr(0,dot); + ext=id.substr(dot+1,(unsigned int)-1); + }else + { + base=id; + } + + int cnt=0; + while (!(!dir->id_to_file(id) && + !dir->name_to_file(id) && + !dir->title_to_file(id))) + { + cnt++; + id=base+"_"+GUTF8String(cnt); + if (ext.length()) + id+="."+ext; + } + return id; +} + +GP<DataPool> +DjVuDocEditor::strip_incl_chunks(const GP<DataPool> & pool_in) +{ + DEBUG_MSG("DjVuDocEditor::strip_incl_chunks() called\n"); + DEBUG_MAKE_INDENT(3); + + const GP<IFFByteStream> giff_in( + IFFByteStream::create(pool_in->get_stream())); + + const GP<ByteStream> gbs_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gbs_out)); + + IFFByteStream &iff_in=*giff_in; + IFFByteStream &iff_out=*giff_out; + + bool have_incl=false; + int chksize; + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } else + { + have_incl=true; + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + } + + if (have_incl) + { + gbs_out->seek(0,SEEK_SET); + return DataPool::create(gbs_out); + } else return pool_in; +} + +GUTF8String +DjVuDocEditor::insert_file(const GURL &file_url, const GUTF8String &parent_id, + int chunk_num, DjVuPort *source) + // Will open the 'file_name' and insert it into an existing DjVuFile + // with ID 'parent_id'. Will insert the INCL chunk at position chunk_num + // Will NOT process ANY files included into the file being inserted. + // Moreover it will strip out any INCL chunks in that file... +{ + DEBUG_MSG("DjVuDocEditor::insert_file(): fname='" << file_url << + "', parent_id='" << parent_id << "'\n"); + DEBUG_MAKE_INDENT(3); + const GP<DjVmDir> dir(get_djvm_dir()); + + if(!source) + source=this; + // Create DataPool and see if the file exists + GP<DataPool> file_pool; + if(file_url.is_empty()||file_url.is_local_file_url()) + { + file_pool=DataPool::create(file_url); + }else + { + file_pool=source->request_data(source, file_url); + if(source != this) + { + file_pool=DataPool::create(file_pool->get_stream()->duplicate()); + } + } + if(file_pool && file_url && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(file_pool,file_url,needs_compression_flag,can_compress_flag); + } + + // Strip any INCL chunks + file_pool=strip_incl_chunks(file_pool); + + // Check if parent ID is valid + GP<DjVmDir::File> parent_frec(dir->id_to_file(parent_id)); + if (!parent_frec) + parent_frec=dir->name_to_file(parent_id); + if (!parent_frec) + parent_frec=dir->title_to_file(parent_id); + if (!parent_frec) + G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t" +parent_id); + const GP<DjVuFile> parent_file(get_djvu_file(parent_id)); + if (!parent_file) + G_THROW( ERR_MSG("DjVuDocEditor.create_fail") "\t"+parent_id); + + // Now obtain ID for the new file + const GUTF8String id(find_unique_id(file_url.fname())); + + // Add it into the directory + const GP<DjVmDir::File> frec( + DjVmDir::File::create(id, id, id, DjVmDir::File::INCLUDE)); + int pos=dir->get_file_pos(parent_frec); + if (pos>=0) + ++pos; + dir->insert_file(frec, pos); + + // Add it to our "cache" + { + const GP<File> f(new File); + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // And insert it into the parent DjVuFile + parent_file->insert_file(id, chunk_num); + + return id; +} + + // First it will insert the 'file_url' at position 'file_pos'. + // + // Then it will process all the INCL chunks in the file and try to do + // the same thing with the included files. If insertion of an included + // file fails, it will proceed with other INCL chunks until it does + // them all. In the very end we will throw exception to let the caller + // know about problems with included files. + // + // If the name of a file being inserted conflicts with some other + // name, which has been in DjVmDir prior to call to this function, + // it will be modified. name2id is the translation table to + // keep track of these modifications. + // + // Also, if a name is in name2id, we will not insert that file again. + // + // Will return TRUE if the file has been successfully inserted. + // FALSE, if the file contains NDIR chunk and has been skipped. +bool +DjVuDocEditor::insert_file(const GURL &file_url, bool is_page, + int & file_pos, GMap<GUTF8String, GUTF8String> & name2id, + DjVuPort *source) +{ + + DEBUG_MSG("DjVuDocEditor::insert_file(): file_url='" << file_url << + "', is_page='" << is_page << "'\n"); + DEBUG_MAKE_INDENT(3); + if (refresh_cb) + refresh_cb(refresh_cl_data); + + + // We do not want to insert the same file twice (important when + // we insert a group of files at the same time using insert_group()) + // So we check if we already did that and return if so. + if (name2id.contains(file_url.fname())) + return true; + + if(!source) + source=this; + + GP<DataPool> file_pool; + if(file_url.is_empty()||file_url.is_local_file_url()) + { + file_pool=DataPool::create(file_url); + } + else + { + file_pool=source->request_data(source, file_url); + if(source != this) + { + file_pool=DataPool::create(file_pool->get_stream()); + } + } + // Create DataPool and see if the file exists + if(file_pool && !file_url.is_empty() && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(file_pool,file_url, + needs_compression_flag, + can_compress_flag); + } + + // Oh. It does exist... Check that it has IFF structure + { + const GP<IFFByteStream> giff( + IFFByteStream::create(file_pool->get_stream())); + IFFByteStream &iff=*giff; + GUTF8String chkid; + + int length; + length=iff.get_chunk(chkid); + if (chkid!="FORM:DJVI" && chkid!="FORM:DJVU" && + chkid!="FORM:BM44" && chkid!="FORM:PM44") + G_THROW( ERR_MSG("DjVuDocEditor.not_1_page") "\t"+file_url.get_string()); + + // Wonderful. It's even a DjVu file. Scan for NDIR chunks. + // If NDIR chunk is found, ignore the file + while(iff.get_chunk(chkid)) + { + if (chkid=="NDIR") + return false; + iff.close_chunk(); + } + } + return insert_file(file_pool,file_url,is_page,file_pos,name2id,source); +} + +bool +DjVuDocEditor::insert_file(const GP<DataPool> &file_pool, + const GURL &file_url, bool is_page, + int & file_pos, GMap<GUTF8String, GUTF8String> & name2id, + DjVuPort *source) +{ + GUTF8String errors; + if(file_pool) + { + const GP<DjVmDir> dir(get_djvm_dir()); + G_TRY + { + // Now get a unique name for this file. + // Check the name2id first... + const GUTF8String name=file_url.fname(); + GUTF8String id; + if (name2id.contains(name)) + { + id=name2id[name]; + }else + { + // Check to see if this page exists with a different name. + if(!is_page) + { + GPList<DjVmDir::File> list(dir->get_files_list()); + for(GPosition pos=list;pos;++pos) + { + DEBUG_MSG("include " << list[pos]->is_include() + << " size=" << list[pos]->size << " length=" + << file_pool->get_length() << "\n"); + if(list[pos]->is_include() + && (!list[pos]->size + || (list[pos]->size == file_pool->get_length()))) + { + id=list[pos]->get_load_name(); + GP<DjVuFile> file(get_djvu_file(id,false)); + const GP<DataPool> pool(file->get_djvu_data(false)); + if(file_pool->simple_compare(*pool)) + { + // The files are the same, so just store the alias. + name2id[name]=id; + } + const GP<IFFByteStream> giff_old(IFFByteStream::create(pool->get_stream())); + const GP<IFFByteStream> giff_new(IFFByteStream::create(file_pool->get_stream())); + file=0; + if(giff_old->compare(*giff_new)) + { + // The files are the same, so just store the alias. + name2id[name]=id; + return true; + } + } + } + } + // Otherwise create a new unique ID and remember the translation + id=find_unique_id(name); + name2id[name]=id; + } + + // Good. Before we continue with the included files we want to + // complete insertion of this one. Notice, that insertion of + // children may fail, in which case we will have to modify + // data for this file to get rid of invalid INCL + + // Create a file record with the chosen ID + const GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id, + is_page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE )); + + // And insert it into the directory + file_pos=dir->insert_file(file, file_pos); + + // And add the File record (containing the file URL and DataPool) + { + const GP<File> f(new File); + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // The file has been added. If it doesn't include anything else, + // that will be enough. Otherwise repeat what we just did for every + // included child. Don't forget to modify the contents of INCL + // chunks due to name2id translation. + // We also want to include here our file with shared annotations, + // if it exists. + GUTF8String chkid; + const GP<IFFByteStream> giff_in( + IFFByteStream::create(file_pool->get_stream())); + IFFByteStream &iff_in=*giff_in; + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + const GP<DjVmDir::File> shared_frec(djvm_dir->get_shared_anno_file()); + + iff_in.get_chunk(chkid); + iff_out.put_chunk(chkid); + while(iff_in.get_chunk(chkid)) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_in.close_chunk(); + iff_out.close_chunk(); + if (shared_frec && chkid=="INFO") + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(shared_frec->get_load_name()); + iff_out.close_chunk(); + } + } else + { + GUTF8String name; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + name+=GUTF8String(buffer, length); + while(isspace(name[0])) + { + name=name.substr(1,(unsigned int)-1); + } + while(isspace(name[(int)name.length()-1])) + { + name.setat(name.length()-1, 0); + } + const GURL::UTF8 full_url(name,file_url.base()); + iff_in.close_chunk(); + + G_TRY { + if (insert_file(full_url, false, file_pos, name2id, source)) + { + // If the child file has been inserted (doesn't + // contain NDIR chunk), add INCL chunk. + GUTF8String id=name2id[name]; + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + } + } G_CATCH(exc) { + // Should an error occur, we move on. INCL chunk will + // not be copied. + if (errors.length()) + errors+="\n\n"; + errors+=exc.get_cause(); + } G_ENDCATCH; + } + } // while(iff_in.get_chunk(chkid)) + iff_out.close_chunk(); + + // Increment the file_pos past the page inserted. + if (file_pos>=0) file_pos++; + + // We have just inserted every included file. We may have modified + // contents of the INCL chunks. So we need to update the DataPool... + gstr_out->seek(0); + const GP<DataPool> new_file_pool(DataPool::create(gstr_out)); + { + // It's important that we replace the pool here anyway. + // By doing this we load the file into memory. And this is + // exactly what insert_group() wants us to do because + // it creates temporary files. + GCriticalSectionLock lock(&files_lock); + files_map[id]->pool=new_file_pool; + } + } G_CATCH(exc) { + if (errors.length()) + errors+="\n\n"; + errors+=exc.get_cause(); + G_THROW(errors); + } G_ENDCATCH; + + // The only place where we intercept exceptions is when we process + // included files. We want to process all of them even if we failed to + // process one. But here we need to let the exception propagate... + if (errors.length()) + G_THROW(errors); + + return true; + } + return false; +} + +void +DjVuDocEditor::insert_group(const GList<GURL> & file_urls, int page_num, + void (* _refresh_cb)(void *), void * _cl_data) + // The function will insert every file from the list at position + // corresponding to page_num. If page_num is negative, concatenation + // will occur. Included files will be processed as well +{ + refresh_cb=_refresh_cb; + refresh_cl_data=_cl_data; + + G_TRY + { + + // First translate the page_num to file_pos. + const GP<DjVmDir> dir(get_djvm_dir()); + int file_pos; + if (page_num<0 || page_num>=dir->get_pages_num()) + { + file_pos=-1; + } + else + { + file_pos=dir->get_page_pos(page_num); + } + + // Now call the insert_file() for every page. We will remember the + // name2id translation table. Thus insert_file() will remember IDs + // it assigned to shared files + GMap<GUTF8String, GUTF8String> name2id; + + GUTF8String errors; + for(GPosition pos=file_urls;pos;++pos) + { + const GURL &furl=file_urls[pos]; + DEBUG_MSG( "Inserting file '" << furl << "'\n" ); + G_TRY + { + // Check if it's a multipage document... + GP<DataPool> xdata_pool(DataPool::create(furl)); + if(xdata_pool && furl.is_valid() + && furl.is_local_file_url() && DjVuDocument::djvu_import_codec) + { + (*DjVuDocument::djvu_import_codec)(xdata_pool,furl, + needs_compression_flag, + can_compress_flag); + } + GUTF8String chkid; + IFFByteStream::create(xdata_pool->get_stream())->get_chunk(chkid); + if (name2id.contains(furl.fname())||(chkid=="FORM:DJVM")) + { + GMap<GUTF8String,void *> map; + map_ids(map); + DEBUG_MSG("Read DjVuDocument furl='" << furl << "'\n"); + GP<ByteStream> gbs(ByteStream::create()); + GP<DjVuDocument> doca(DjVuDocument::create_noinit()); + doca->set_verbose_eof(verbose_eof); + doca->set_recover_errors(recover_errors); + doca->init(furl /* ,this */ ); + doca->wait_for_complete_init(); + get_portcaster()->add_route(doca,this); + DEBUG_MSG("Saving DjVuDocument url='" << furl << "' with unique names\n"); + doca->write(gbs,map); + gbs->seek(0L); + DEBUG_MSG("Loading unique names\n"); + GP<DjVuDocument> doc(DjVuDocument::create(gbs)); + doc->set_verbose_eof(verbose_eof); + doc->set_recover_errors(recover_errors); + doc->wait_for_complete_init(); + get_portcaster()->add_route(doc,this); + gbs=0; + DEBUG_MSG("Inserting pages\n"); + int pages_num=doc->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + const GURL url(doc->page_to_url(page_num)); + insert_file(url, true, file_pos, name2id, doc); + } + } + else + { + insert_file(furl, true, file_pos, name2id, this); + } + } G_CATCH(exc) + { + if (errors.length()) + { + errors+="\n\n"; + } + errors+=exc.get_cause(); + } + G_ENDCATCH; + } + if (errors.length()) + { + G_THROW(errors); + } + } G_CATCH_ALL + { + refresh_cb=0; + refresh_cl_data=0; + G_RETHROW; + } G_ENDCATCH; + refresh_cb=0; + refresh_cl_data=0; +} + +void +DjVuDocEditor::insert_page(const GURL &file_url, int page_num) +{ + DEBUG_MSG("DjVuDocEditor::insert_page(): furl='" << file_url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GList<GURL> list; + list.append(file_url); + + insert_group(list, page_num); +} + +void +DjVuDocEditor::insert_page(GP<DataPool> & _file_pool, + const GURL & file_url, int page_num) + // Use _file_pool as source of data, create a new DjVuFile + // with name file_name, and insert it as page number page_num +{ + DEBUG_MSG("DjVuDocEditor::insert_page(): pool size='" << + _file_pool->get_size() << "'\n"); + DEBUG_MAKE_INDENT(3); + + const GP<DjVmDir> dir(get_djvm_dir()); + + // Strip any INCL chunks (we do not allow to insert hierarchies + // using this function) + const GP<DataPool> file_pool(strip_incl_chunks(_file_pool)); + + // Now obtain ID for the new file + const GUTF8String id(find_unique_id(file_url.fname())); + + // Add it into the directory + const GP<DjVmDir::File> frec(DjVmDir::File::create( + id, id, id, DjVmDir::File::PAGE)); + int pos=dir->get_page_pos(page_num); + dir->insert_file(frec, pos); + + // Add it to our "cache" + { + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } +} + +void +DjVuDocEditor::generate_ref_map(const GP<DjVuFile> & file, + GMap<GUTF8String, void *> & ref_map, + GMap<GURL, void *> & visit_map) + // This private function is used to generate a list (implemented as map) + // of files referencing the given file. To get list of all parents + // for file with ID 'id' iterate map obtained as + // *((GMap<GUTF8String, void *> *) ref_map[id]) +{ + const GURL url=file->get_url(); + const GUTF8String id(djvm_dir->name_to_file(url.fname())->get_load_name()); + if (!visit_map.contains(url)) + { + visit_map[url]=0; + + GPList<DjVuFile> files_list=file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVuFile> child_file=files_list[pos]; + // First: add the current file to the list of parents for + // the child being processed + GURL child_url=child_file->get_url(); + const GUTF8String child_id( + djvm_dir->name_to_file(child_url.fname())->get_load_name()); + GMap<GUTF8String, void *> * parents=0; + if (ref_map.contains(child_id)) + parents=(GMap<GUTF8String, void *> *) ref_map[child_id]; + else + ref_map[child_id]=parents=new GMap<GUTF8String, void *>(); + (*parents)[id]=0; + // Second: go recursively + generate_ref_map(child_file, ref_map, visit_map); + } + } +} + +void +DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref, + GMap<GUTF8String, void *> & ref_map) + // Private function, which will remove file with ID id. + // + // If will also remove all INCL chunks in parent files pointing + // to this one + // + // Finally, if remove_unref is TRUE, we will go down the files + // hierarchy removing every file, which becomes unreferenced. + // + // ref_map will be used to find out list of parents referencing + // this file (required when removing INCL chunks) +{ + // First get rid of INCL chunks in parents + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[id]; + if (parents) + { + for(GPosition pos=*parents;pos;++pos) + { + const GUTF8String parent_id((*parents).key(pos)); + const GP<DjVuFile> parent(get_djvu_file(parent_id)); + if (parent) + parent->unlink_file(id); + } + delete parents; + parents=0; + ref_map.del(id); + } + + // We will accumulate errors here. + GUTF8String errors; + + // Now modify the ref_map and process children if necessary + GP<DjVuFile> file=get_djvu_file(id); + if (file) + { + G_TRY { + GPList<DjVuFile> files_list=file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVuFile> child_file=files_list[pos]; + GURL child_url=child_file->get_url(); + const GUTF8String child_id( + djvm_dir->name_to_file(child_url.fname())->get_load_name()); + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[child_id]; + if (parents) parents->del(id); + + if (remove_unref && (!parents || !parents->size())) + remove_file(child_id, remove_unref, ref_map); + } + } G_CATCH(exc) { + if (errors.length()) errors+="\n\n"; + errors+=exc.get_cause(); + } G_ENDCATCH; + } + + // Finally remove this file from the directory. + djvm_dir->delete_file(id); + + // And get rid of its thumbnail, if any + GCriticalSectionLock lock(&thumb_lock); + GPosition pos(thumb_map.contains(id)); + if (pos) + { + thumb_map.del(pos); + } + if (errors.length()) + G_THROW(errors); +} + +void +DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_file(): id='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!djvm_dir->id_to_file(id)) + G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t"+id); + + // First generate a map of references (containing the list of parents + // including this particular file. This will speed things up + // significatly. + GMap<GUTF8String, void *> ref_map; // GMap<GUTF8String, GMap<GUTF8String, void *> *> in fact + GMap<GURL, void *> visit_map; // To avoid loops + + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + generate_ref_map(get_djvu_file(page_num), ref_map, visit_map); + + // Now call the function, which will do the removal recursively + remove_file(id, remove_unref, ref_map); + + // And clear the ref_map + GPosition pos; + while((pos=ref_map)) + { + GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[pos]; + delete parents; + ref_map.del(pos); + } +} + +void +DjVuDocEditor::remove_page(int page_num, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_page(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + // Translate the page_num to ID + GP<DjVmDir> djvm_dir=get_djvm_dir(); + if (page_num<0 || page_num>=djvm_dir->get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + // And call general remove_file() + remove_file(djvm_dir->page_to_file(page_num)->get_load_name(), remove_unref); +} + +void +DjVuDocEditor::remove_pages(const GList<int> & page_list, bool remove_unref) +{ + DEBUG_MSG("DjVuDocEditor::remove_pages() called\n"); + DEBUG_MAKE_INDENT(3); + + // First we need to translate page numbers to IDs (they will + // obviously be changing while we're removing pages one after another) + GP<DjVmDir> djvm_dir=get_djvm_dir(); + GPosition pos ; + if (djvm_dir) + { + GList<GUTF8String> id_list; + for(pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + id_list.append(frec->get_load_name()); + } + + for(pos=id_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + remove_page(frec->get_page_num(), remove_unref); + } + } +} + +void +DjVuDocEditor::move_file(const GUTF8String &id, int & file_pos, + GMap<GUTF8String, void *> & map) + // NOTE! file_pos here is the desired position in DjVmDir *after* + // the record with ID 'id' is removed. +{ + if (!map.contains(id)) + { + map[id]=0; + + GP<DjVmDir::File> file_rec=djvm_dir->id_to_file(id); + if (file_rec) + { + file_rec=new DjVmDir::File(*file_rec); + djvm_dir->delete_file(id); + djvm_dir->insert_file(file_rec, file_pos); + + if (file_pos>=0) + { + file_pos++; + + // We care to move included files only if we do not append + // This is because the only reason why we move included + // files is to made them available sooner than they would + // be available if we didn't move them. By appending files + // we delay the moment when the data for the file becomes + // available, of course. + GP<DjVuFile> djvu_file=get_djvu_file(id); + if (djvu_file) + { + GPList<DjVuFile> files_list=djvu_file->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + { + const GUTF8String name(files_list[pos]->get_url().fname()); + GP<DjVmDir::File> child_frec=djvm_dir->name_to_file(name); + + // If the child is positioned in DjVmDir AFTER the + // file being processed (position is file_pos or greater), + // move it to file_pos position + if (child_frec) + if (djvm_dir->get_file_pos(child_frec)>file_pos) + move_file(child_frec->get_load_name(), file_pos, map); + } + } + } + } + } +} + +void +DjVuDocEditor::move_page(int page_num, int new_page_num) +{ + DEBUG_MSG("DjVuDocEditor::move_page(): page_num=" << page_num << + ", new_page_num=" << new_page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num==new_page_num) return; + + int pages_num=get_pages_num(); + if (page_num<0 || page_num>=pages_num) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + const GUTF8String id(page_to_id(page_num)); + int file_pos=-1; + if (new_page_num>=0 && new_page_num<pages_num) + if (new_page_num>page_num) // Moving toward the end + { + if (new_page_num<pages_num-1) + file_pos=djvm_dir->get_page_pos(new_page_num+1)-1; + } else + file_pos=djvm_dir->get_page_pos(new_page_num); + + GMap<GUTF8String, void *> map; + move_file(id, file_pos, map); +} +#ifdef _WIN32_WCE_EMULATION // Work around odd behavior under WCE Emulation +#define CALLINGCONVENTION __cdecl +#else +#define CALLINGCONVENTION /* */ +#endif + +static int +CALLINGCONVENTION +cmp(const void * ptr1, const void * ptr2) +{ + int num1=*(int *) ptr1; + int num2=*(int *) ptr2; + return num1<num2 ? -1 : num1>num2 ? 1 : 0; +} + +static GList<int> +sortList(const GList<int> & list) +{ + GArray<int> a(list.size()-1); + int cnt; + GPosition pos; + for(pos=list, cnt=0;pos;++pos, cnt++) + a[cnt]=list[pos]; + + qsort((int *) a, a.size(), sizeof(int), cmp); + + GList<int> l; + for(int i=0;i<a.size();i++) + l.append(a[i]); + + return l; +} + +void +DjVuDocEditor::move_pages(const GList<int> & _page_list, int shift) +{ + if (!shift) return; + + GList<int> page_list=sortList(_page_list); + + GList<GUTF8String> id_list; + for(GPosition pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + id_list.append(frec->get_load_name()); + } + + if (shift<0) + { + // We have to start here from the smallest page number + // We will move it according to the 'shift', and all + // further moves are guaranteed not to affect its page number. + + // We will be changing the 'min_page' to make sure that + // pages moved beyond the document will still be in correct order + int min_page=0; + for(GPosition pos=id_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + { + int page_num=frec->get_page_num(); + int new_page_num=page_num+shift; + if (new_page_num<min_page) + new_page_num=min_page++; + move_page(page_num, new_page_num); + } + } + } else + { + // We have to start here from the biggest page number + // We will move it according to the 'shift', and all + // further moves will not affect its page number. + + // We will be changing the 'max_page' to make sure that + // pages moved beyond the document will still be in correct order + int max_page=djvm_dir->get_pages_num()-1; + for(GPosition pos=id_list.lastpos();pos;--pos) + { + GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]); + if (frec) + { + int page_num=frec->get_page_num(); + int new_page_num=page_num+shift; + if (new_page_num>max_page) + new_page_num=max_page--; + move_page(page_num, new_page_num); + } + } + } +} + +void +DjVuDocEditor::set_file_name(const GUTF8String &id, const GUTF8String &name) +{ + DEBUG_MSG("DjVuDocEditor::set_file_name(), id='" << id << "', name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + // It's important to get the URL now, because later (after we + // change DjVmDir) id_to_url() will be returning a modified value + GURL url=id_to_url(id); + + // Change DjVmDir. It will check if the name is unique + djvm_dir->set_file_name(id, name); + + // Now find DjVuFile (if any) and rename it + GPosition pos; + if (files_map.contains(id, pos)) + { + GP<File> file=files_map[pos]; + GP<DataPool> pool=file->pool; + if (pool) pool->load_file(); + GP<DjVuFile> djvu_file=file->file; + if (djvu_file) djvu_file->set_name(name); + } +} + +void +DjVuDocEditor::set_page_name(int page_num, const GUTF8String &name) +{ + DEBUG_MSG("DjVuDocEditor::set_page_name(), page_num='" << page_num << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + set_file_name(page_to_id(page_num), name); +} + +void +DjVuDocEditor::set_file_title(const GUTF8String &id, const GUTF8String &title) +{ + DEBUG_MSG("DjVuDocEditor::set_file_title(), id='" << id << "', title='" << title << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Just change DjVmDir. It will check if the title is unique + djvm_dir->set_file_title(id, title); +} + +void +DjVuDocEditor::set_page_title(int page_num, const GUTF8String &title) +{ + DEBUG_MSG("DjVuDocEditor::set_page_title(), page_num='" << page_num << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (page_num<0 || page_num>=get_pages_num()) + G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num)); + + set_file_title(page_to_id(page_num), title); +} + +//**************************************************************************** +//************************** Shared annotations ****************************** +//**************************************************************************** + +void +DjVuDocEditor::simplify_anno(void (* progress_cb)(float progress, void *), + void * cl_data) + // It's important that no decoding is done while this function + // is running. Otherwise the DjVuFile's decoding routines and + // this function may attempt to decode/modify a file's + // annotations at the same time. +{ + // Get the name of the SHARED_ANNO file. We will not + // touch that file (will not move annotations from it) + GP<DjVmDir::File> shared_file=djvm_dir->get_shared_anno_file(); + GUTF8String shared_id; + if (shared_file) + shared_id=shared_file->get_load_name(); + + GList<GURL> ignore_list; + if (shared_id.length()) + ignore_list.append(id_to_url(shared_id)); + + // First, for every page get merged (or "flatten" or "projected") + // annotations and store them inside the top-level page file + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + GP<DjVuFile> djvu_file=get_djvu_file(page_num); + if (!djvu_file) + G_THROW( ERR_MSG("DjVuDocEditor.page_fail") "\t"+page_num); + int max_level=0; + GP<ByteStream> anno; + anno=djvu_file->get_merged_anno(ignore_list, &max_level); + if (anno && max_level>0) + { + // This is the moment when we try to modify DjVuFile's annotations + // Make sure, that it's not being decoded + GSafeFlags & file_flags=djvu_file->get_safe_flags(); + GMonitorLock lock(&file_flags); + while(file_flags & DjVuFile::DECODING) + file_flags.wait(); + + // Merge all chunks in one by decoding and encoding DjVuAnno + const GP<DjVuAnno> dec_anno(DjVuAnno::create()); + dec_anno->decode(anno); + const GP<ByteStream> new_anno(ByteStream::create()); + dec_anno->encode(new_anno); + new_anno->seek(0); + + // And store it in the file + djvu_file->anno=new_anno; + djvu_file->rebuild_data_pool(); + if ((file_flags & (DjVuFile::DECODE_OK | + DjVuFile::DECODE_FAILED | + DjVuFile::DECODE_STOPPED))==0) + djvu_file->anno=0; + } + if (progress_cb) + progress_cb((float)(page_num/2.0/pages_num), cl_data); + } + + // Now remove annotations from every file except for + // the top-level page files and SHARED_ANNO file. + // Unlink empty files too. + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + int cnt; + GPosition pos; + for(pos=files_list, cnt=0;pos;++pos, cnt++) + { + GP<DjVmDir::File> frec=files_list[pos]; + if (!frec->is_page() && frec->get_load_name()!=shared_id) + { + GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name()); + if (djvu_file) + { + djvu_file->remove_anno(); + if (djvu_file->get_chunks_number()==0) + remove_file(frec->get_load_name(), true); + } + } + if (progress_cb) + progress_cb((float)(0.5+cnt/2.0/files_list.size()), cl_data); + } +} + +void +DjVuDocEditor::create_shared_anno_file(void (* progress_cb)(float progress, void *), + void * cl_data) +{ + if (djvm_dir->get_shared_anno_file()) + G_THROW( ERR_MSG("DjVuDocEditor.share_fail") ); + + // Prepare file with ANTa chunk inside + const GP<ByteStream> gstr(ByteStream::create()); + const GP<IFFByteStream> giff(IFFByteStream::create(gstr)); + IFFByteStream &iff=*giff; + iff.put_chunk("FORM:DJVI"); + iff.put_chunk("ANTa"); + iff.close_chunk(); + iff.close_chunk(); + ByteStream &str=*gstr; + str.flush(); + str.seek(0); + const GP<DataPool> file_pool(DataPool::create(gstr)); + + // Get a unique ID for the new file + const GUTF8String id(find_unique_id("shared_anno.iff")); + + // Add it into the directory + GP<DjVmDir::File> frec(DjVmDir::File::create(id, id, id, + DjVmDir::File::SHARED_ANNO)); + djvm_dir->insert_file(frec, 1); + + // Add it to our "cache" + { + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + } + + // Now include this shared file into every top-level page file + int pages_num=djvm_dir->get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + GP<DjVuFile> djvu_file=get_djvu_file(page_num); + djvu_file->insert_file(id, 1); + + if (progress_cb) + progress_cb((float) page_num/pages_num, cl_data); + } +} + +void +DjVuDocEditor::set_djvm_nav(GP<DjVmNav> n) +{ + if (n && ! n->isValidBookmark()) + G_THROW("Invalid bookmark data"); + djvm_nav = n; +} + +GP<DjVuFile> +DjVuDocEditor::get_shared_anno_file(void) +{ + GP<DjVuFile> djvu_file; + + GP<DjVmDir::File> frec=djvm_dir->get_shared_anno_file(); + if (frec) + djvu_file=get_djvu_file(frec->get_load_name()); + + return djvu_file; +} + +GP<DataPool> +DjVuDocEditor::get_thumbnail(int page_num, bool dont_decode) + // We override DjVuDocument::get_thumbnail() here because + // pages may have been shuffled and those "thumbnail file records" + // from the DjVmDir do not describe things correctly. + // + // So, first we will check the thumb_map[] if we have a predecoded + // thumbnail for the given page. If this is the case, we will + // return it. Otherwise we will ask DjVuDocument to generate + // this thumbnail for us. +{ + const GUTF8String id(page_to_id(page_num)); + + GCriticalSectionLock lock(&thumb_lock); + const GPosition pos(thumb_map.contains(id)); + if (pos) + { + // Get the image from the map + return thumb_map[pos]; + } else + { + unfile_thumbnails(); + return DjVuDocument::get_thumbnail(page_num, dont_decode); + } +} + +int +DjVuDocEditor::get_thumbnails_num(void) const +{ + GCriticalSectionLock lock((GCriticalSection *) &thumb_lock); + + int cnt=0; + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + if (thumb_map.contains(page_to_id(page_num))) + cnt++; + } + return cnt; +} + +int +DjVuDocEditor::get_thumbnails_size(void) const +{ + DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock((GCriticalSection *) &thumb_lock); + + int pages_num=get_pages_num(); + for(int page_num=0;page_num<pages_num;page_num++) + { + const GPosition pos(thumb_map.contains(page_to_id(page_num))); + if (pos) + { + const GP<ByteStream> gstr(thumb_map[pos]->get_stream()); + GP<IW44Image> iwpix=IW44Image::create_decode(IW44Image::COLOR); + iwpix->decode_chunk(gstr); + + int width=iwpix->get_width(); + int height=iwpix->get_height(); + return width<height ? width : height; + } + } + return -1; +} + +void +DjVuDocEditor::remove_thumbnails(void) +{ + DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + unfile_thumbnails(); + + DEBUG_MSG("clearing thumb_map\n"); + GCriticalSectionLock lock(&thumb_lock); + thumb_map.empty(); +} + +void +DjVuDocEditor::unfile_thumbnails(void) + // Will erase all "THUMBNAILS" files from DjVmDir. + // This function is useful when filing thumbnails (to get rid of + // those files, which currently exist: they need to be replaced + // anyway) and when calling DjVuDocument::get_thumbnail() to + // be sure, that it will not use wrong information from DjVmDir +{ + DEBUG_MSG("DjVuDocEditor::unfile_thumbnails(): updating DjVmDir\n"); + DEBUG_MAKE_INDENT(3); + + { + GCriticalSectionLock lock(&threqs_lock); + threqs_list.empty(); + } + if((const DjVmDir *)djvm_dir) + { + GPList<DjVmDir::File> xfiles_list=djvm_dir->get_files_list(); + for(GPosition pos=xfiles_list;pos;++pos) + { + GP<DjVmDir::File> f=xfiles_list[pos]; + if (f->is_thumbnails()) + djvm_dir->delete_file(f->get_load_name()); + } + } +} + +void +DjVuDocEditor::file_thumbnails(void) + // The purpose of this function is to create files containing + // thumbnail images and register them in DjVmDir. + // If some of the thumbnail images are missing, they'll + // be generated with generate_thumbnails() +{ + DEBUG_MSG("DjVuDocEditor::file_thumbnails(): updating DjVmDir\n"); + DEBUG_MAKE_INDENT(3); + unfile_thumbnails(); + + // Generate thumbnails if they're missing due to some reason. + int thumb_num=get_thumbnails_num(); + int size=thumb_num>0 ? get_thumbnails_size() : 128; + if (thumb_num!=get_pages_num()) + { + generate_thumbnails(size); + } + + DEBUG_MSG("filing thumbnails\n"); + + GCriticalSectionLock lock(&thumb_lock); + + // The first thumbnail file always contains only one thumbnail + int ipf=1; + int image_num=0; + int page_num=0, pages_num=djvm_dir->get_pages_num(); + GP<ByteStream> str(ByteStream::create()); + GP<IFFByteStream> iff(IFFByteStream::create(str)); + iff->put_chunk("FORM:THUM"); + for(;;) + { + GUTF8String id(page_to_id(page_num)); + const GPosition pos(thumb_map.contains(id)); + if (! pos) + { + G_THROW( ERR_MSG("DjVuDocEditor.no_thumb") "\t"+GUTF8String(page_num)); + } + iff->put_chunk("TH44"); + iff->copy(*(thumb_map[pos]->get_stream())); + iff->close_chunk(); + image_num++; + page_num++; + if (image_num>=ipf || page_num>=pages_num) + { + int i=id.rsearch('.'); + if(i<=0) + { + i=id.length(); + } + id=id.substr(0,i)+".thumb"; + // Get unique ID for this file + id=find_unique_id(id); + + // Create a file record with the chosen ID + GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id, + DjVmDir::File::THUMBNAILS)); + + // Set correct file position (so that it will cover the next + // ipf pages) + int file_pos=djvm_dir->get_page_pos(page_num-image_num); + djvm_dir->insert_file(file, file_pos); + + // Now add the File record (containing the file URL and DataPool) + // After we do it a simple save_as() will save the document + // with the thumbnails. This is because DjVuDocument will see + // the file in DjVmDir and will ask for data. We will intercept + // the request for data and will provide this DataPool + iff->close_chunk(); + str->seek(0); + const GP<DataPool> file_pool(DataPool::create(str)); + GP<File> f=new File; + f->pool=file_pool; + GCriticalSectionLock lock(&files_lock); + files_map[id]=f; + + // And create new streams + str=ByteStream::create(); + iff=IFFByteStream::create(str); + iff->put_chunk("FORM:THUM"); + image_num=0; + + // Reset ipf to correct value (after we stored first + // "exceptional" file with thumbnail for the first page) + if (page_num==1) ipf=thumbnails_per_file; + if (page_num>=pages_num) break; + } + } +} + +int +DjVuDocEditor::generate_thumbnails(int thumb_size, int page_num) +{ + DEBUG_MSG("DjVuDocEditor::generate_thumbnails(): doing it\n"); + DEBUG_MAKE_INDENT(3); + + if(page_num<(djvm_dir->get_pages_num())) + { + const GUTF8String id(page_to_id(page_num)); + if (!thumb_map.contains(id)) + { + const GP<DjVuImage> dimg(get_page(page_num, true)); + + GRect rect(0, 0, thumb_size, dimg->get_height()*thumb_size/dimg->get_width()); + GP<GPixmap> pm=dimg->get_pixmap(rect, rect, get_thumbnails_gamma()); + if (!pm) + { + const GP<GBitmap> bm(dimg->get_bitmap(rect, rect, sizeof(int))); + if (bm) + pm = GPixmap::create(*bm); + else + pm = GPixmap::create(rect.height(), rect.width(), &GPixel::WHITE); + } + // Store and compress the pixmap + const GP<IW44Image> iwpix(IW44Image::create_encode(*pm)); + const GP<ByteStream> gstr(ByteStream::create()); + IWEncoderParms parms; + parms.slices=97; + parms.bytes=0; + parms.decibels=0; + iwpix->encode_chunk(gstr, parms); + gstr->seek(0L); + thumb_map[id]=DataPool::create(gstr); + } + ++page_num; + } + else + { + page_num = -1; + } + return page_num; +} + +void +DjVuDocEditor::generate_thumbnails(int thumb_size, + bool (* cb)(int page_num, void *), + void * cl_data) +{ + int page_num=0; + do + { + page_num=generate_thumbnails(thumb_size,page_num); + if (cb) if (cb(page_num, cl_data)) return; + } while(page_num>=0); +} + +static void +store_file(const GP<DjVmDir> & src_djvm_dir, const GP<DjVmDoc> & djvm_doc, + GP<DjVuFile> & djvu_file, GMap<GURL, void *> & map) +{ + GURL url=djvu_file->get_url(); + if (!map.contains(url)) + { + map[url]=0; + + // Store included files first + GPList<DjVuFile> djvu_files_list=djvu_file->get_included_files(false); + for(GPosition pos=djvu_files_list;pos;++pos) + store_file(src_djvm_dir, djvm_doc, djvu_files_list[pos], map); + + // Now store contents of this file + GP<DataPool> file_data=djvu_file->get_djvu_data(false); + GP<DjVmDir::File> frec=src_djvm_dir->name_to_file(url.name()); + if (frec) + { + frec=new DjVmDir::File(*frec); + djvm_doc->insert_file(frec, file_data, -1); + } + } +} + +void +DjVuDocEditor::save_pages_as( + const GP<ByteStream> &str, const GList<int> & _page_list) +{ + GList<int> page_list=sortList(_page_list); + + GP<DjVmDoc> djvm_doc=DjVmDoc::create(); + GMap<GURL, void *> map; + for(GPosition pos=page_list;pos;++pos) + { + GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]); + if (frec) + { + GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name()); + if (djvu_file) + store_file(djvm_dir, djvm_doc, djvu_file, map); + } + } + djvm_doc->write(str); +} + +void +DjVuDocEditor::save_file(const GUTF8String &file_id, const GURL &codebase, + const bool only_modified, GMap<GUTF8String,GUTF8String> & map) +{ + if(only_modified) + { + for(GPosition pos=files_map;pos;++pos) + { + const GP<File> file_rec(files_map[pos]); + const bool file_modified=file_rec->pool || + (file_rec->file && file_rec->file->is_modified()); + if(!file_modified) + { + const GUTF8String id=files_map.key(pos); + const GUTF8String save_name(djvm_dir->id_to_file(id)->get_save_name()); + if(id == save_name) + { + map[id]=id; + } + } + } + } + save_file(file_id,codebase,map); +} + +void +DjVuDocEditor::save_file( + const GUTF8String &file_id, const GURL &codebase, + GMap<GUTF8String,GUTF8String> & map) +{ + DEBUG_MSG("DjVuDocEditor::save_file(): ID='" << file_id << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!map.contains(file_id)) + { + const GP<DjVmDir::File> file(djvm_dir->id_to_file(file_id)); + + GP<DataPool> file_pool; + const GPosition pos(files_map.contains(file_id)); + if (pos) + { + const GP<File> file_rec(files_map[pos]); + if (file_rec->file) + file_pool=file_rec->file->get_djvu_data(false); + else + file_pool=file_rec->pool; + } + + if (!file_pool) + { + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + file_pool=pcaster->request_data(this, id_to_url(file_id)); + } + + if (file_pool) + { + GMap<GUTF8String,GUTF8String> incl; + map[file_id]=get_djvm_doc()->save_file(codebase,*file,incl,file_pool); + for(GPosition pos=incl;pos;++pos) + { + save_file(incl.key(pos),codebase ,map); + } + }else + { + map[file_id]=file->get_save_name(); + } + } +} + +void +DjVuDocEditor::save(void) +{ + DEBUG_MSG("DjVuDocEditor::save(): saving the file\n"); + DEBUG_MAKE_INDENT(3); + + if (!can_be_saved()) + G_THROW( ERR_MSG("DjVuDocEditor.cant_save") ); + save_as(GURL(), orig_doc_type!=INDIRECT); +} + +void +DjVuDocEditor::write(const GP<ByteStream> &gbs, bool force_djvm) +{ + DEBUG_MSG("DjVuDocEditor::write()\n"); + DEBUG_MAKE_INDENT(3); + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + clean_files_map(); + DjVuDocument::write(gbs,force_djvm); +} + +void +DjVuDocEditor::write( + const GP<ByteStream> &gbs,const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVuDocEditor::write()\n"); + DEBUG_MAKE_INDENT(3); + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + clean_files_map(); + DjVuDocument::write(gbs,reserved); +} + +void +DjVuDocEditor::save_as(const GURL &where, bool bundled) +{ + DEBUG_MSG("DjVuDocEditor::save_as(): where='" << where << "'\n"); + DEBUG_MAKE_INDENT(3); + + // First see if we need to generate (or just reshuffle) thumbnails... + // If we have an icon for every page, we will just call + // file_thumbnails(), which will update DjVmDir and will create + // the actual bundles with thumbnails (very fast) + // Otherwise we will remove the thumbnails completely because + // we really don't want to deal with documents, which have only + // some of their pages thumbnailed. + if (get_thumbnails_num()==get_pages_num()) + { + file_thumbnails(); + }else + { + remove_thumbnails(); + } + + GURL save_doc_url; + + if (where.is_empty()) + { + // Assume, that we just want to 'save'. Check, that it's possible + // and proceed. + bool can_be_saved_bundled=orig_doc_type==BUNDLED || + orig_doc_type==OLD_BUNDLED || + orig_doc_type==SINGLE_PAGE || + orig_doc_type==OLD_INDEXED && orig_doc_pages==1; + if ((bundled ^ can_be_saved_bundled)!=0) + G_THROW( ERR_MSG("DjVuDocEditor.cant_save2") ); + save_doc_url=doc_url; + } else + { + save_doc_url=where; + } + + int save_doc_type=bundled ? BUNDLED : INDIRECT; + + clean_files_map(); + + GCriticalSectionLock lock(&files_lock); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + // First consider saving in SINGLE_FILE format (one file) + if(needs_compression()) + { + DEBUG_MSG("Compressing on output\n"); + remove_thumbnails(); + if(! djvu_compress_codec) + { + G_THROW( ERR_MSG("DjVuDocEditor.no_codec") ); + } + const GP<DjVmDoc> doc(get_djvm_doc()); + GP<ByteStream> mbs(ByteStream::create()); + doc->write(mbs); + mbs->flush(); + mbs->seek(0,SEEK_SET); + djvu_compress_codec(mbs,save_doc_url,(!(const DjVmDir *)djvm_dir)||(djvm_dir->get_files_num()==1)||(save_doc_type!=INDIRECT)); + files_map.empty(); + doc_url=GURL(); + }else + { + if (djvm_dir->get_files_num()==1) + { + // Here 'bundled' has no effect: we will save it as one page. + DEBUG_MSG("saving one file...\n"); + GURL file_url=page_to_url(0); + const GUTF8String file_id(djvm_dir->page_to_file(0)->get_load_name()); + GP<DataPool> file_pool; + GPosition pos=files_map.contains(file_id); + if (pos) + { + const GP<File> file_rec(files_map[pos]); + if (file_rec->pool && (!file_rec->file || + !file_rec->file->is_modified())) + { + file_pool=file_rec->pool; + }else if (file_rec->file) + { + file_pool=file_rec->file->get_djvu_data(false); + } + } + // Even if file has not been modified (pool==0) we still want + // to save it. + if (!file_pool) + file_pool=pcaster->request_data(this, file_url); + if (file_pool) + { + DEBUG_MSG("Saving '" << file_url << "' to '" << save_doc_url << "'\n"); + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr_out(ByteStream::create(save_doc_url, "wb")); + ByteStream &str_out=*gstr_out; + str_out.writall(octets, 4); + const GP<ByteStream> str_in(file_pool->get_stream()); + str_out.copy(*str_in); + } + + // Update the document's DataPool (to save memory) + const GP<DjVmDoc> doc(get_djvm_doc()); + const GP<ByteStream> gstr=ByteStream::create();// One page: we can do it in the memory + doc->write(gstr); + gstr->seek(0, SEEK_SET); + const GP<DataPool> pool(DataPool::create(gstr)); + doc_pool=pool; + init_data_pool=pool; + + // Also update DjVmDir (to reflect changes in offsets) + djvm_dir=doc->get_djvm_dir(); + } else if (save_doc_type==INDIRECT) + { + DEBUG_MSG("Saving in INDIRECT format to '" << save_doc_url << "'\n"); + bool save_only_modified=!(save_doc_url!=doc_url || save_doc_type!=orig_doc_type); + GPList<DjVmDir::File> xfiles_list=djvm_dir->resolve_duplicates(false); + const GURL codebase=save_doc_url.base(); + int pages_num=djvm_dir->get_pages_num(); + GMap<GUTF8String, GUTF8String> map; + // First go thru the pages + for(int page_num=0;page_num<pages_num;page_num++) + { + const GUTF8String id(djvm_dir->page_to_file(page_num)->get_load_name()); + save_file(id, codebase, save_only_modified, map); + } + // Next go thru thumbnails and similar stuff + GPosition pos; + for(pos=xfiles_list;pos;++pos) + save_file(xfiles_list[pos]->get_load_name(), codebase, save_only_modified, map); + + // Finally - save the top-level index file + for(pos=xfiles_list;pos;++pos) + { + const GP<DjVmDir::File> file(xfiles_list[pos]); + file->offset=0; + file->size=0; + } + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb")); + const GP<IFFByteStream> giff(IFFByteStream::create(gstr)); + IFFByteStream &iff=*giff; + + iff.put_chunk("FORM:DJVM", 1); + iff.put_chunk("DIRM"); + djvm_dir->encode(giff->get_bytestream()); + iff.close_chunk(); + iff.close_chunk(); + iff.flush(); + + // Update the document data pool (not required, but will save memory) + doc_pool=DataPool::create(save_doc_url); + init_data_pool=doc_pool; + + // No reason to update DjVmDir as for this format it doesn't + // contain DJVM offsets + } else if (save_doc_type==BUNDLED || save_doc_type==OLD_BUNDLED) + { + DEBUG_MSG("Saving in BUNDLED format to '" << save_doc_url << "'\n"); + + // Can't be very smart here. Simply overwrite the file. + const GP<DjVmDoc> doc(get_djvm_doc()); + DataPool::load_file(save_doc_url); + const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb")); + doc->write(gstr); + gstr->flush(); + + // Update the document data pool (not required, but will save memory) + doc_pool=DataPool::create(save_doc_url); + init_data_pool=doc_pool; + + // Also update DjVmDir (to reflect changes in offsets) + djvm_dir=doc->get_djvm_dir(); + } else + { + G_THROW( ERR_MSG("DjVuDocEditor.cant_save") ); + } + + // Now, after we have saved the document w/o any error, detach DataPools, + // which are in the 'File's list to save memory. Detach everything. + // Even in the case when File->file is non-zero. If File->file is zero, + // remove the item from the list at all. If it's non-zero, it has + // to stay there because by definition files_map[] contains the list + // of all active files and customized DataPools + // + // In addition to it, look thru all active files and change their URLs + // to reflect changes in the document's URL (if there was a change) + // Another reason why file's URLs must be changed is that we may have + // saved the document in a different format, which changes the rules + // of file url composition. + for(GPosition pos=files_map;pos;) + { + const GP<File> file_rec(files_map[pos]); + file_rec->pool=0; + if (file_rec->file==0) + { + GPosition this_pos=pos; + ++pos; + files_map.del(this_pos); + } else + { + // Change the file's url; + if (doc_url!=save_doc_url || + orig_doc_type!=save_doc_type) + if (save_doc_type==BUNDLED) + file_rec->file->move(save_doc_url); + else file_rec->file->move(save_doc_url.base()); + ++pos; + } + } + + } + orig_doc_type=save_doc_type; + doc_type=save_doc_type; + + if (doc_url!=save_doc_url) + { + // Also update document's URL (we moved, didn't we?) + doc_url=save_doc_url; + init_url=save_doc_url; + } +} + +GP<DjVuDocEditor> +DjVuDocEditor::create_wait(void) +{ + DjVuDocEditor *doc=new DjVuDocEditor(); + const GP<DjVuDocEditor> retval(doc); + doc->init(); + return retval; +} + +GP<DjVuDocEditor> +DjVuDocEditor::create_wait(const GURL &url) +{ + DjVuDocEditor *doc=new DjVuDocEditor(); + const GP<DjVuDocEditor> retval(doc); + doc->init(url); + return retval; +} + +bool +DjVuDocEditor::inherits(const GUTF8String &class_name) const +{ + return (class_name == "DjVuDocEditor")||DjVuDocument::inherits(class_name); +} + +int +DjVuDocEditor::get_orig_doc_type(void) const +{ + return orig_doc_type; +} + +bool +DjVuDocEditor::can_be_saved(void) const +{ + return !(needs_rename()||needs_compression()||orig_doc_type==UNKNOWN_TYPE || + orig_doc_type==OLD_INDEXED); +} + +int +DjVuDocEditor::get_save_doc_type(void) const +{ + if (orig_doc_type==SINGLE_PAGE) + if (djvm_dir->get_files_num()==1) + return SINGLE_PAGE; + else + return BUNDLED; + else if (orig_doc_type==INDIRECT) + return INDIRECT; + else if (orig_doc_type==OLD_BUNDLED || orig_doc_type==BUNDLED) + return BUNDLED; + else + return UNKNOWN_TYPE; +} + +GURL +DjVuDocEditor::get_doc_url(void) const +{ + return doc_url.is_empty() ? init_url : doc_url; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h new file mode 100644 index 00000000..7bf6124a --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h @@ -0,0 +1,460 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocEditor.h,v 1.9 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDOCEDITOR_H +#define _DJVUDOCEDITOR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuDocument.h" +#include "DjVmDoc.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name DjVuDocEditor.h + Files #"DjVuDocEditor.h"# and #"DjVuDocEditor.cpp"# contain extension + of \Ref{DjVuDocument} class, which can create and modify existing + DjVu document, generate thumbnails, etc. It does {\bf not} do + compression though. + + @memo DjVu document editor class. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVuDocEditor.h,v 1.9 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** #DjVuDocEditor# is an extension of \Ref{DjVuDocument} class with + additional capabilities for editing the document contents. + + It can be used to: + \begin{enumerate} + \item Create (compose) new multipage DjVu documents using single + page DjVu documents. The class does {\bf not} do compression. + \item Insert and remove different pages of multipage DjVu documents. + \item Change attributes ({\em names}, {\em IDs} and {\em titles}) + of files composing the DjVu document. + \item Generate thumbnail images and integrate them into the document. + \end{enumerate} +*/ + +class DjVuDocEditor : public DjVuDocument +{ +public: + static int thumbnails_per_file; + +protected: + /// Default constructor + DjVuDocEditor(void); + + /** Initialization function. Initializes an empty document. + + {\bf Note}: You must call either of the two + available \Ref{init}() function before you start doing + anything else with the #DjVuDocEditor#. */ + void init(void); + + /** Initialization function. Opens document with name #filename#. + + {\bf Note}: You must call either of the two + available \Ref{init}() function before you start doing + anything else with the #DjVuDocEditor#. */ + void init(const GURL &url); + +public: + /** Creates a DjVuDocEditor class and initializes with #fname#. */ + static GP<DjVuDocEditor> create_wait(const GURL &url); + + /** Creates a DjVuDocEditor class and initializes an empty document. */ + static GP<DjVuDocEditor> create_wait(void); + + + /// Destructor + virtual ~DjVuDocEditor(void); + + /** Returns type of open document. #DjVuDocEditor# silently + converts any open DjVu document to #BUNDLED# format (see + \Ref{DjVuDocument}. Thus, \Ref{DjVuDocument::get_doc_type}() + will always be returning #BUNDLED#. Use this function to + learn the original format of the document being edited. */ + int get_orig_doc_type(void) const; + + /** Returns #TRUE# if the document can be "saved" (sometimes + the only possibility is to do a "save as"). The reason why + we have this function is that #DjVuDocEditor# can save + documents in new formats only (#BUNDLED# and #INDIRECT#). + At the same time it recognizes all DjVu formats (#OLD_BUNDLED#, + #OLD_INDEXED#, #BUNDLED#, and #INDIRECT#). + + #OLD_BUNDLED# and #BUNDLED# documents occupy only one file, + so in this case "saving" involves the automatic conversion + to #BUNDLED# format and storing data into the same file. + + #OLD_INDEXED# documents, on the other hand, occupy more + than one file. They could be converted to #INDIRECT# format + if these two formats had the same set of files. Unfortunately, + these formats are too different, and the best thing to do + is to use "save as" capability. */ + bool can_be_saved(void) const; + + /** Returns type of the document, which can be created by + \Ref{save}() function. Can be #INDIRECT#, #BUNDLED#, + #SINGLE_PAGE#, or #UNKNOWN_TYPE#. The latter indicates, + that \Ref{save}() will fail, and that \Ref{save_as}() + should be used instead */ + int get_save_doc_type(void) const; + + /** Saves the document. May generate exception if the document + can not be saved, and \Ref{save_as}() should be used. + See \Ref{can_be_saved}() for details. */ + void save(void); + + /** Saves the document. */ + virtual void save_as(const GURL &where, bool bundled); + + /** Saves the document in the {\em new bundled} format. All the data + is "bundled" into one file and this file is written into the + passed stream. + + If #force_djvm# is #TRUE# then even one page documents will be + saved in the #DJVM BUNDLED# format (inside a #FORM:DJVM#); + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + virtual void write(const GP<ByteStream> &str, bool force_djvm=false); + /** Always save as bundled, renaming any files conflicting with the + the names in the supplied GMap. */ + virtual void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *> &reserved); + + /** Saves the specified pages in DjVu #BUNDLED# multipage document. */ + void save_pages_as( + const GP<ByteStream> &str, const GList<int> & page_list); + + /** Translates page number #page_num# to ID. If #page_num# is invalid, + an exception is thrown. */ + GUTF8String page_to_id(int page_num) const; + + GUTF8String insert_file(const GURL &url, const GUTF8String &parent_id, + int chunk_num=1, DjVuPort *source=0); + /** Inserts the referenced file into this DjVu document. + + @param fname Name of the top-level file containing the image of + the page to be inserted. This file must be a DjVu file and + may include one or more other DjVu files. + + If it include other DjVu files, the function will try to + insert them into the document too. Should this attempt fail, + the corresponding #INCL# chunk will be removed from the + referencing file and an exception will be thrown. + + When inserting a file, the function may modify its name + to be unique in the DjVu document. + @param page_num Position where the new page should be inserted at. + Negative value means "append" */ + void insert_page(const GURL &fname, int page_num=-1); + /** Inserts a new page with data inside the #data_pool# as page + number #page_num. + + @param data_pool \Ref{DataPool} with data for this page. + @param file_name Name, which will be assigned to this page. + If you try to save the document in #INDIRECT# format, + a file with this name will be created to hold the + page's data. If there is already a file in the document + with the same name, the function will derive a new + unique name from file_name, which will be assigned + to the page. + @param page_num Describes where the page should be inserted. + Negative number means "append". */ + void insert_page(GP<DataPool> & file_pool, + const GURL &fname, int page_num=-1); + /** Inserts a group of pages into this DjVu document. + + Like \Ref{insert_page}() it will insert every page into the document. + The main advantage of calling this function once for the whole + group instead of calling \Ref{insert_page}() for every page is + the processing of included files: + + The group of files may include one or more files, which are thus + shared by them. If you call \Ref{insert_page}() for every page, + this shared file will be inserted into the document more than once + though under different names. This is how \Ref{insert_page}() works: + whenever it inserts something, it checks for duplicate names with + only one purpose: invent a new name if a given one is already in + use. + + On the other hand, if you call #insert_group#(), it will insert + shared included files only once. This is because it can analyze + the group of files before inserting them and figure out what files + are shared and thus should be inserted only once. + + @param fname_list List of top-level files for the pages to be inserted + @param page_num Position where the new pages should be inserted at. + Negative value means "append" */ + void insert_group(const GList<GURL> & furl_list, int page_num=-1, + void (* refresh_cb)(void *)=0, void * cl_data=0); + /** Removes the specified page from the document. If #remove_unref# + is #TRUE#, the function will also remove from the document any file, + which became unreferenced due to the page's removal */ + void remove_page(int page_num, bool remove_unref=true); + /** Removes the specified pages from the document. If #remove_unref# + is #TRUE#, the function will also remove from the document any file, + which became unreferenced due to the pages' removal */ + void remove_pages(const GList<int> & page_list, bool remove_unref=true); + /** Removes a DjVu file with the specified #id#. + + If some other files include this file, the corresponding #INCL# + chunks will be removed to avoid dead links. + + If #remove_unref# is #TRUE#, the function will also remove every + file, which will become unreferenced after the removal of this file. */ + void remove_file(const GUTF8String &id, bool remove_unref=true); + /** Makes page number #page_num# to be #new_page_num#. If #new_page_num# + is negative or too big, the function will move page #page_num# to + the end of the document. */ + void move_page(int page_num, int new_page_num); + /** Shifts all pags from the #page_list# according to the #shift#. + The #shift# can be positive (shift toward the end of the document) + or negative (shift toward the beginning of the document). + + It is OK to make #shift# too big in value. Pages will just be + moved to the end (or to the beginning, depending on the #shift# + sign) of the document. */ + void move_pages(const GList<int> & page_list, int shift); + + /** Changes the name of the file with ID #id# to #name#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_file_name(const GUTF8String &id, const GUTF8String &name); + /** Changes the name of the page #page_num# to #name#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_page_name(int page_num, const GUTF8String &name); + /** Changes the title of the file with ID #id# to #title#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_file_title(const GUTF8String &id, const GUTF8String &title); + /** Changes the title of the page #page_num# to #title#. + Refer to \Ref{DjVmDir} for the explanation of {\em IDs}, + {\em names} and {\em titles}. */ + void set_page_title(int page_num, const GUTF8String &title); + + /** @name Thumbnails */ + //@{ + /** Returns the number of thumbnails stored inside this document. + + It may be #ZERO#, which means, that there are no thumbnails at all. + + It may be equal to the number of pages, which is what should + normally be. + + Finally, it may be greater than #ZERO# and less than the number + of pages, in which case thumbnails should be regenerated before + the document can be saved. */ + int get_thumbnails_num(void) const; + + /** Returns the size of the first encountered thumbnail image. Since + thumbnails can currently be generated by \Ref{generate_thumbnails}() + only, all thumbnail images should be of the same size. Thus, + the number returned is actually the size of {\em all} + document thumbnails. + + The function will return #-1# if there are no thumbnails. */ + int get_thumbnails_size(void) const; + + /** Removes all thumbnails from the document */ + void remove_thumbnails(void); + + /** Generates thumbnails for the specified page, if and only if + it does not have a thumbnail yet. If you want to regenerate + thumbnails for all pages, call \Ref{remove_thumbnails}() prior + to calling this function. + + @param thumb_size The size of the thumbnails in pixels. DjVu viewer + is able to rescale the thumbnail images if necessary, so this + parameter affects thumbnails quality only. 128 is a good number. + @param page_num The page number to genate the thumbnail for. */ + int generate_thumbnails(int thumb_size, int page_num); + + /** Generates thumbnails for those pages, which do not have them yet. + If you want to regenerate thumbnails for all pages, call + \Ref{remove_thumbnails}() prior to calling this function. + + @param thumb_size The size of the thumbnails in pixels. DjVu viewer + is able to rescale the thumbnail images if necessary, so this + parameter affects thumbnails quality only. 128 is a good number. + @param cb The callback, which will be called after thumbnail image + for the next page has been generated. Regardless of if + the document already has thumbnail images for some of its + pages, the callback will be called #pages_num# times, where + #pages_num# is the total number of pages in the document. + The callback should return #FALSE# if thumbnails generating + should proceed. #TRUE# will stop it. */ + void generate_thumbnails(int thumb_size, + bool (* cb)(int page_num, void *)=0, + void * cl_data=0); + //@} + /** Use this function to simplify annotations in the document. + The "simplified" format is when annotations are only allowed + either in top-level page files or in a special file with + #SHARED_ANNO# flag on. This file is supposed to be included into + every page. */ + void simplify_anno(void (* progress_cb)(float progress, void *)=0, + void * cl_data=0); + + /** Will create a file that will be included into every page and + marked with the #SHARED_ANNO# flag. This file can be used + to store global annotations (annotations applicable to every page). + + {\bf Note:} There may be only one #SHARED_ANNO# file in any + DjVu multipage document. */ + void create_shared_anno_file(void (* progress_cb)(float progress, void *)=0, + void * cl_data=0); + + /** Sets bookmark data */ + void set_djvm_nav(GP<DjVmNav> nav); + + /** Returns a pointer to the file with #SHARED_ANNO# flag on. + This file should be used for storing document-wide annotations. + + {\bf Note:} There may be only one #SHARED_ANNO# file in any + DjVu multipage document. */ + GP<DjVuFile> get_shared_anno_file(void); + + GURL get_doc_url(void) const; + + /** Returns TRUE if #class_name# is #"DjVuDocEditor"#, + #"DjVuDocument"# or #"DjVuPort"# */ + virtual bool inherits(const GUTF8String &class_name) const; + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); +protected: + virtual GP<DjVuFile> url_to_file(const GURL & url, bool dont_create) const; + virtual GP<DataPool> get_thumbnail(int page_num, bool dont_decode); + friend class CThumbNails; +public: + class File; +private: + bool initialized; + GURL doc_url; + GP<DataPool> doc_pool; + GURL tmp_doc_url; + int orig_doc_type; + int orig_doc_pages; + + GPMap<GUTF8String, File> files_map; // files_map[id]=GP<File> + GCriticalSection files_lock; + + GPMap<GUTF8String,DataPool> thumb_map; + GCriticalSection thumb_lock; + + void (* refresh_cb)(void *); + void * refresh_cl_data; + + void check(void); + GUTF8String find_unique_id(GUTF8String id); + GP<DataPool> strip_incl_chunks(const GP<DataPool> & pool); + void clean_files_map(void); + bool insert_file_type(const GURL &file_url, + DjVmDir::File::FILE_TYPE page_type, + int & file_pos, + GMap<GUTF8String, GUTF8String> & name2id); + bool insert_file( const GP<DataPool> &pool, + const GURL &file_url, bool is_page, + int & file_pos, + GMap<GUTF8String,GUTF8String> & name2id, + DjVuPort *source=0 ); + bool insert_file( + const GURL &file_url, bool is_page, + int & file_pos, + GMap<GUTF8String,GUTF8String> & name2id, + DjVuPort *source=0 ); + void remove_file(const GUTF8String &id, bool remove_unref, + GMap<GUTF8String, void *> & ref_map); + void generate_ref_map(const GP<DjVuFile> & file, + GMap<GUTF8String, void *> & ref_map, + GMap<GURL, void *> & visit_map); + void move_file(const GUTF8String &id, int & file_pos, + GMap<GUTF8String, void *> & map); + void unfile_thumbnails(void); + void file_thumbnails(void); + void save_file(const GUTF8String &id, const GURL &codebase, + const bool only_modified, GMap<GUTF8String, GUTF8String> & map); + void save_file(const GUTF8String &id, const GURL &codebase, + GMap<GUTF8String, GUTF8String> & map); +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp new file mode 100644 index 00000000..3b33d943 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp @@ -0,0 +1,1845 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocument.cpp,v 1.13 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDocument.h" +#include "DjVmDoc.h" +#include "DjVmDir0.h" +#include "DjVmNav.h" +#include "DjVuNavDir.h" +#include "DjVuImage.h" +#include "DjVuFileCache.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "GRect.h" + +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char octets[4]={0x41,0x54,0x26,0x54}; +const float DjVuDocument::thumb_gamma=(float)2.20; + +void (* DjVuDocument::djvu_import_codec)( + GP<DataPool> &pool, const GURL &url, bool &needs_compression, + bool &needs_rename )=0; + +void (* DjVuDocument::djvu_compress_codec)( + GP<ByteStream> &doc,const GURL &where,bool bundled)=0; + +void +DjVuDocument::set_import_codec( + void (*codec)( + GP<DataPool> &pool, const GURL &url, bool &needs_compression, bool &needs_rename )) +{ + djvu_import_codec=codec; +} + +void +DjVuDocument::set_compress_codec( + void (* codec)( + GP<ByteStream> &doc,const GURL &where,bool bundled)) +{ + djvu_compress_codec=codec; +} + +DjVuDocument::DjVuDocument(void) + : doc_type(UNKNOWN_TYPE), + needs_compression_flag(false), + can_compress_flag(false), + needs_rename_flag(false), + has_url_names(false), + recover_errors(ABORT), + verbose_eof(false), + init_started(false), + cache(0) +{ +} + +GP<DjVuDocument> +DjVuDocument::create( + GP<DataPool> pool, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + DjVuDocument *doc=new DjVuDocument; + GP<DjVuDocument> retval=doc; + doc->init_data_pool=pool; + doc->start_init(GURL(),xport,xcache); + return retval; +} + +GP<DjVuDocument> +DjVuDocument::create( + const GP<ByteStream> &bs, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + return create(DataPool::create(bs),xport,xcache); +} + +GP<DjVuDocument> +DjVuDocument::create_wait( + const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + GP<DjVuDocument> retval=create(url,xport,xcache); + retval->wait_for_complete_init(); + return retval; +} + +void +DjVuDocument::start_init( + const GURL & url, GP<DjVuPort> xport, DjVuFileCache * xcache) +{ + DEBUG_MSG("DjVuDocument::start_init(): initializing class...\n"); + DEBUG_MAKE_INDENT(3); + if (init_started) + G_THROW( ERR_MSG("DjVuDocument.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuDocument.not_secure") ); + if(url.is_empty()) + { + if (!init_data_pool) + G_THROW( ERR_MSG("DjVuDocument.empty_url") ); + if(init_url.is_empty()) + { + init_url=invent_url("document.djvu"); + } + }else + { + init_url=url; + } + + // Initialize + cache=xcache; + doc_type=UNKNOWN_TYPE; + DjVuPortcaster * pcaster=get_portcaster(); + if (!xport) + xport=simple_port=new DjVuSimplePort(); + pcaster->add_route(this, xport); + pcaster->add_route(this, this); + + if(!url.is_empty()) + { + init_data_pool=pcaster->request_data(this, init_url); + if(init_data_pool) + { + if(!init_url.is_empty() && init_url.is_local_file_url() && djvu_import_codec) + { + djvu_import_codec(init_data_pool,init_url,needs_compression_flag,needs_rename_flag); + } + if(needs_rename_flag) + can_compress_flag=true; + } + if (!init_data_pool) + { + G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+init_url.get_string()); + } + } + // Now we say it is ready + init_started=true; + + init_thread_flags=STARTED; + init_life_saver=this; + init_thr.create(static_init_thread, this); +} + +DjVuDocument::~DjVuDocument(void) +{ + // No more messages, please. We're being destroyed. + get_portcaster()->del_port(this); + + // We want to stop any DjVuFile which has been created by us + // and is still being decoded. We have to stop them manually because + // they keep the "life saver" in the decoding thread and won't stop + // when we clear the last reference to them + { + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<DjVuFile> file=ufiles_list[pos]->file; + file->stop_decode(false); + file->stop(false); // Disable any access to data + } + ufiles_list.empty(); + } + + GPList<DjVuPort> ports=get_portcaster()->prefix_to_ports(get_int_prefix()); + for(GPosition pos=ports;pos;++pos) + { + GP<DjVuPort> port=ports[pos]; + if (port->inherits("DjVuFile")) + { + DjVuFile * file=(DjVuFile *) (DjVuPort *) port; + file->stop_decode(false); + file->stop(false); // Disable any access to data + } + } + DataPool::close_all(); +} + +void +DjVuDocument::stop_init(void) +{ + DEBUG_MSG("DjVuDocument::stop_init(): making sure that the init thread dies.\n"); + DEBUG_MAKE_INDENT(3); + + GMonitorLock lock(&init_thread_flags); + while((init_thread_flags & STARTED) && + !(init_thread_flags & FINISHED)) + { + if (init_data_pool) init_data_pool->stop(true); // blocking operation + + if (ndir_file) ndir_file->stop(false); + + { + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + ufiles_list[pos]->file->stop(false); // Disable any access to data + ufiles_list.empty(); + } + + init_thread_flags.wait(50); + } +} + +void +DjVuDocument::check() const +{ + if (!init_started) + G_THROW( ERR_MSG("DjVuDocument.not_init") ); +} + +void +DjVuDocument::static_init_thread(void * cl_data) +{ + DjVuDocument * th=(DjVuDocument *) cl_data; + GP<DjVuDocument> life_saver=th; + th->init_life_saver=0; + G_TRY { + th->init_thread(); + } G_CATCH(exc) { + th->flags|=DjVuDocument::DOC_INIT_FAILED; + G_TRY { + th->check_unnamed_files(); + if (!exc.cmp_cause(ByteStream::EndOfFile) && th->verbose_eof) + get_portcaster()->notify_error(th, ERR_MSG("DjVuDocument.init_eof") ); + else if (!exc.cmp_cause(DataPool::Stop)) + get_portcaster()->notify_status(th, ERR_MSG("DjVuDocument.stopped") ); + else + get_portcaster()->notify_error(th, exc.get_cause()); + } G_CATCH_ALL {} G_ENDCATCH; + th->init_thread_flags|=FINISHED; + } G_ENDCATCH; +} + +void +DjVuDocument::init_thread(void) + // This function is run in a separate thread. + // The goal is to detect the document type (BUNDLED, OLD_INDEXED, etc.) + // and decode navigation directory. +{ + DEBUG_MSG("DjVuDocument::init_thread(): guessing what we're dealing with\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + GP<ByteStream> stream=init_data_pool->get_stream(); + + GP<IFFByteStream> giff=IFFByteStream::create(stream); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int size=iff.get_chunk(chkid); + if (!size) + G_THROW( ByteStream::EndOfFile ); + if (size < 0) + G_THROW( ERR_MSG("DjVuDocument.no_file") ); + if (size<8) + { + G_THROW( ERR_MSG("DjVuDocument.not_DjVu") ); + } + if (chkid=="FORM:DJVM") + { + DEBUG_MSG("Got DJVM document here\n"); + DEBUG_MAKE_INDENT(3); + + size=iff.get_chunk(chkid); + if (chkid=="DIRM") + { + djvm_dir=DjVmDir::create(); + djvm_dir->decode(iff.get_bytestream()); + iff.close_chunk(); + if (djvm_dir->is_bundled()) + { + DEBUG_MSG("Got BUNDLED file.\n"); + doc_type=BUNDLED; + } + else + { + DEBUG_MSG("Got INDIRECT file.\n"); + doc_type=INDIRECT; + } + flags|=DOC_TYPE_KNOWN | DOC_DIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN | DOC_DIR_KNOWN, 0); + check_unnamed_files(); + + /* Check for NAVM */ + size=iff.get_chunk(chkid); + if (size && chkid=="NAVM") + { + djvm_nav=DjVmNav::create(); + djvm_nav->decode(iff.get_bytestream()); + iff.close_chunk(); + } + } + else if (chkid=="DIR0") + { + DEBUG_MSG("Got OLD_BUNDLED file.\n"); + doc_type=OLD_BUNDLED; + flags|=DOC_TYPE_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0); + check_unnamed_files(); + } + else + G_THROW( ERR_MSG("DjVuDocument.bad_format") ); + + if (doc_type==OLD_BUNDLED) + { + // Read the DjVmDir0 directory. We are unable to tell what + // files are pages and what are included at this point. + // We only know that the first file with DJVU (BM44 or PM44) + // form *is* the first page. The rest will become known + // after we decode DjVuNavDir + djvm_dir0=DjVmDir0::create(); + djvm_dir0->decode(*iff.get_bytestream()); + iff.close_chunk(); + // Get offset to the first DJVU, PM44 or BM44 chunk + int first_page_offset=0; + while(!first_page_offset) + { + int offset; + size=iff.get_chunk(chkid, &offset); + if (size==0) G_THROW( ERR_MSG("DjVuDocument.no_page") ); + if (chkid=="FORM:DJVU" || chkid=="FORM:PM44" || chkid=="FORM:BM44") + { + DEBUG_MSG("Got 1st page offset=" << offset << "\n"); + first_page_offset=offset; + } + iff.close_chunk(); + } + + // Now get the name of this file + int file_num; + for(file_num=0;file_num<djvm_dir0->get_files_num();file_num++) + { + DjVmDir0::FileRec & file=*djvm_dir0->get_file(file_num); + if (file.offset==first_page_offset) + { + first_page_name=file.name; + break; + } + } + if (!first_page_name.length()) + G_THROW( ERR_MSG("DjVuDocument.no_page") ); + flags|=DOC_DIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_DIR_KNOWN, 0); + check_unnamed_files(); + } + } + else // chkid!="FORM:DJVM" + { + // DJVU format + DEBUG_MSG("Got DJVU OLD_INDEXED or SINGLE_PAGE document here.\n"); + doc_type=SINGLE_PAGE; + flags|=DOC_TYPE_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0); + check_unnamed_files(); + } + if (doc_type==OLD_BUNDLED || doc_type==SINGLE_PAGE) + { + DEBUG_MSG("Searching for NDIR chunks...\n"); + ndir_file=get_djvu_file(-1); + if (ndir_file) ndir=ndir_file->decode_ndir(); + ndir_file=0; // Otherwise ~DjVuDocument() will stop (=kill) it + if (!ndir) + { + // Seems to be 1-page old-style document. Create dummy NDIR + if (doc_type==OLD_BUNDLED) + { + ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url)); + ndir->insert_page(-1, first_page_name); + } + else + { + ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url.base())); + ndir->insert_page(-1, init_url.fname()); + } + } + else + { + if (doc_type==SINGLE_PAGE) + doc_type=OLD_INDEXED; + } + flags|=DOC_NDIR_KNOWN; + pcaster->notify_doc_flags_changed(this, DOC_NDIR_KNOWN, 0); + check_unnamed_files(); + } + + flags|=DOC_INIT_OK; + pcaster->notify_doc_flags_changed(this, DOC_INIT_OK, 0); + check_unnamed_files(); + init_thread_flags|=FINISHED; + DEBUG_MSG("DOCUMENT IS FULLY INITIALIZED now: doc_type='" << + (doc_type==BUNDLED ? "BUNDLED" : + doc_type==OLD_BUNDLED ? "OLD_BUNDLED" : + doc_type==INDIRECT ? "INDIRECT" : + doc_type==OLD_INDEXED ? "OLD_INDEXED" : + doc_type==SINGLE_PAGE ? "SINGLE_PAGE" : + "UNKNOWN") << "'\n"); +} + +bool +DjVuDocument::wait_for_complete_init(void) +{ + flags.enter(); + while(!(flags & DOC_INIT_FAILED) && + !(flags & DOC_INIT_OK)) flags.wait(); + flags.leave(); + init_thread_flags.enter(); + while (!(init_thread_flags & FINISHED)) + init_thread_flags.wait(); + init_thread_flags.leave(); + return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0; +} + +int +DjVuDocument::wait_get_pages_num(void) const +{ + GSafeFlags &f=const_cast<GSafeFlags &>(flags); + f.enter(); + while(!(f & DOC_TYPE_KNOWN) && + !(f & DOC_INIT_FAILED) && + !(f & DOC_INIT_OK)) f.wait(); + f.leave(); + return get_pages_num(); +} + +GUTF8String +DjVuDocument::get_int_prefix(void) const +{ + // These NAMEs are used to enable DjVuFile sharing inside the same + // DjVuDocument using DjVuPortcaster. Since URLs are unique to the + // document, other DjVuDocuments cannot retrieve files until they're + // assigned some permanent name. After '?' there should be the real + // file's URL. Please note, that output of this function is used only + // as name for DjVuPortcaster. Not as a URL. + GUTF8String retval; + return retval.format("document_%p%d?", this, hash(init_url)); +} + +void +DjVuDocument::set_file_aliases(const DjVuFile * file) +{ + DEBUG_MSG("DjVuDocument::set_file_aliases(): setting global aliases for file '" + << file->get_url() << "'\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + GMonitorLock lock(&((DjVuFile *) file)->get_safe_flags()); + pcaster->clear_aliases(file); + if (file->is_decode_ok() && cache) + { + // If file is successfully decoded and caching is enabled, + // assign a global alias to this file, so that any other + // DjVuDocument will be able to use it. + + pcaster->add_alias(file, file->get_url().get_string()); + if (flags & (DOC_NDIR_KNOWN | DOC_DIR_KNOWN)) + { + int page_num=url_to_page(file->get_url()); + if (page_num>=0) + { + if (page_num==0) pcaster->add_alias(file, init_url.get_string()+"#-1"); + pcaster->add_alias(file, init_url.get_string()+"#"+GUTF8String(page_num)); + } + } + // The following line MUST stay here. For OLD_INDEXED documents + // a page may finish decoding before DIR or NDIR becomes known + // (multithreading, remember), so the code above would not execute + pcaster->add_alias(file, file->get_url().get_string()+"#-1"); + } else pcaster->add_alias(file, get_int_prefix()+file->get_url()); +} + +void +DjVuDocument::check_unnamed_files(void) +{ + DEBUG_MSG("DjVuDocument::check_unnamed_files(): Seeing if we can fix some...\n"); + DEBUG_MAKE_INDENT(3); + + if (flags & DOC_INIT_FAILED) + { + // Init failed. All unnamed files should be terminated + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<DjVuFile> file=ufiles_list[pos]->file; + file->stop_decode(true); + file->stop(false); // Disable any access to data + } + ufiles_list.empty(); + return; + } + + if ((flags & DOC_TYPE_KNOWN)==0) + return; + + // See the list of unnamed files (created when there was insufficient + // information about DjVuDocument structure) and try to fix those, + // which can be fixed at this time + while(true) + { + DjVuPortcaster * pcaster=get_portcaster(); + + GP<UnnamedFile> ufile; + GURL new_url; + GPosition pos ; + GCriticalSectionLock lock(&ufiles_lock); + for(pos=ufiles_list;pos;) + { + G_TRY + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->id_type==UnnamedFile::ID) + new_url=id_to_url(f->id); + else + new_url=page_to_url(f->page_num); + if (!new_url.is_empty()) + { + ufile=f; + // Don't take it off the list. We want to be + // able to stop the init from ~DjVuDocument(); + // + // ufiles_list.del(pos); + break; + } else if (is_init_complete()) + { + // No empty URLs are allowed at this point. + // We now know all information about the document + // and can determine if a page is inside it or not + f->data_pool->set_eof(); + GUTF8String msg; + if (f->id_type==UnnamedFile::ID) + msg= ERR_MSG("DjVuDocument.miss_page_name") "\t"+f->id; + else + msg= ERR_MSG("DjVuDocument.miss_page_num") "\t"+GUTF8String(f->page_num); + G_THROW(msg); + } + ++pos; + } + G_CATCH(exc) + { + pcaster->notify_error(this, exc.get_cause()); + GP<DataPool> pool=ufiles_list[pos]->data_pool; + if (pool) + pool->stop(); + GPosition this_pos=pos; + ++pos; + ufiles_list.del(this_pos); + } + G_ENDCATCH; + } + + if (ufile && !new_url.is_empty()) + { + DEBUG_MSG("Fixing file: '" << ufile->url << "'=>'" << new_url << "'\n"); + // Now, once we know its real URL we can request a real DataPool and + // can connect the DataPool owned by DjVuFile to that real one + // Note, that now request_data() will not play fool because + // we have enough information + + G_TRY + { + if (ufile->data_pool) + { + GP<DataPool> new_pool=pcaster->request_data(ufile->file, new_url); + if(!new_pool) + G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+new_url.get_string()); + ufile->data_pool->connect(new_pool); + } + ufile->file->set_name(new_url.fname()); + ufile->file->move(new_url.base()); + set_file_aliases(ufile->file); + } + G_CATCH(exc) + { + pcaster->notify_error(this, exc.get_cause()); + } + G_ENDCATCH; + } + else + break; + + // Remove the 'ufile' from the list + for(pos=ufiles_list;pos;++pos) + if (ufiles_list[pos]==ufile) + { + ufiles_list.del(pos); + break; + } + } // while(1) +} + +int +DjVuDocument::get_pages_num(void) const +{ + check(); + if (flags & DOC_TYPE_KNOWN) + if (doc_type==BUNDLED || doc_type==INDIRECT) + return djvm_dir->get_pages_num(); + else if (flags & DOC_NDIR_KNOWN) + return ndir->get_pages_num(); + return 1; +} + +GURL +DjVuDocument::page_to_url(int page_num) const +{ + check(); + DEBUG_MSG("DjVuDocument::page_to_url(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GURL url; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case SINGLE_PAGE: + case OLD_INDEXED: + { + if (page_num<0) url=init_url; + else if (flags & DOC_NDIR_KNOWN) url=ndir->page_to_url(page_num); + break; + } + case OLD_BUNDLED: + { + if (page_num<0) page_num=0; + if (page_num==0 && (flags & DOC_DIR_KNOWN)) + url=GURL::UTF8(first_page_name,init_url); + else if (flags & DOC_NDIR_KNOWN) + url=ndir->page_to_url(page_num); + break; + } + case BUNDLED: + { + if (page_num<0) + page_num=0; + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num); + if (!file) G_THROW( ERR_MSG("DjVuDocument.big_num") ); + url=GURL::UTF8(file->get_load_name(),init_url); + } + break; + } + case INDIRECT: + { + if (page_num<0) page_num=0; + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num); + if (!file) + G_THROW( ERR_MSG("DjVuDocument.big_num") ); + url=GURL::UTF8(file->get_load_name(),init_url.base()); + } + break; + } + default: + G_THROW( ERR_MSG("DjVuDocument.unk_type") ); + } + return url; +} + +int +DjVuDocument::url_to_page(const GURL & url) const +{ + check(); + DEBUG_MSG("DjVuDocument::url_to_page(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + int page_num=-1; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case SINGLE_PAGE: + case OLD_BUNDLED: + case OLD_INDEXED: + { + if (flags & DOC_NDIR_KNOWN) page_num=ndir->url_to_page(url); + break; + } + case BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file; + if (url.base()==init_url) + file=djvm_dir->id_to_file(url.fname()); + if (file) + page_num=file->get_page_num(); + } + break; + } + case INDIRECT: + { + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file; + if (url.base()==init_url.base()) + file=djvm_dir->id_to_file(url.fname()); + if (file) + page_num=file->get_page_num(); + } + break; + } + default: + G_THROW( ERR_MSG("DjVuDocument.unk_type") ); + } + return page_num; +} + +GURL +DjVuDocument::id_to_url(const GUTF8String & id) const +{ + check(); + DEBUG_MSG("DjVuDocument::id_to_url(): translating ID='" << id << "' to URL\n"); + DEBUG_MAKE_INDENT(3); + + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case BUNDLED: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->id_to_file(id); + if (!file) + { + file=djvm_dir->name_to_file(id); + if (!file) + file=djvm_dir->title_to_file(id); + } + if (file) + return GURL::UTF8(file->get_load_name(),init_url); + } + break; + case INDIRECT: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir::File> file=djvm_dir->id_to_file(id); + if (!file) + { + file=djvm_dir->name_to_file(id); + if (!file) + file=djvm_dir->title_to_file(id); + } + if (file) + return GURL::UTF8(file->get_load_name(),init_url.base()); + } + break; + case OLD_BUNDLED: + if (flags & DOC_DIR_KNOWN) + { + GP<DjVmDir0::FileRec> frec=djvm_dir0->get_file(id); + if (frec) + return GURL::UTF8(id,init_url); + } + break; + case OLD_INDEXED: + case SINGLE_PAGE: + return GURL::UTF8(id,init_url.base()); + break; + } + return GURL(); +} + +GURL +DjVuDocument::id_to_url(const DjVuPort * source, const GUTF8String &id) +{ + return id_to_url(id); +} + +GP<DjVuFile> +DjVuDocument::url_to_file(const GURL & url, bool dont_create) const + // This function is private and is called from two places: + // id_to_file() and get_djvu_file() ONLY when the structure is known +{ + check(); + DEBUG_MSG("DjVuDocument::url_to_file(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Try DjVuPortcaster to find existing files. + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + GP<DjVuPort> port; + + if (cache) + { + // First - fully decoded files + port=pcaster->alias_to_port(url.get_string()); + if (port && port->inherits("DjVuFile")) + { + DEBUG_MSG("found fully decoded file using DjVuPortcaster\n"); + return (DjVuFile *) (DjVuPort *) port; + } + } + + // Second - internal files + port=pcaster->alias_to_port(get_int_prefix()+url); + if (port && port->inherits("DjVuFile")) + { + DEBUG_MSG("found internal file using DjVuPortcaster\n"); + return (DjVuFile *) (DjVuPort *) port; + } + + GP<DjVuFile> file; + + if (!dont_create) + { + DEBUG_MSG("creating a new file\n"); + file=DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof); + const_cast<DjVuDocument *>(this)->set_file_aliases(file); + } + + return file; +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(int page_num, bool dont_create) const +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): request for page " << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=DjVuPort::get_portcaster(); + + GURL url; + { + // I'm locking the flags because depending on what page_to_url() + // returns me, I'll be creating DjVuFile in different ways. + // And I don't want the situation to change between the moment I call + // id_to_url() and I actually create DjVuFile + GMonitorLock lock(&(const_cast<DjVuDocument *>(this)->flags)); + url=page_to_url(page_num); + if (url.is_empty()) + { + // If init is complete and url is empty, we know for sure, that + // smth is wrong with the page_num. So we can return ZERO. + // Otherwise we create a temporary file and wait for init to finish + if (is_init_complete()) return 0; + + DEBUG_MSG("Structure is not known => check <doc_url>#<page_num> alias...\n"); + GP<DjVuPort> port; + if (cache) + port=pcaster->alias_to_port(init_url.get_string()+"#"+GUTF8String(page_num)); + if (!port || !port->inherits("DjVuFile")) + { + DEBUG_MSG("failed => invent dummy URL and proceed\n"); + + // Invent some dummy temporary URL. I don't care what it will + // be. I'll remember the page_num and will generate the correct URL + // after I learn what the document is + GUTF8String name("page"); + name+=GUTF8String(page_num); + name+=".djvu"; + url=invent_url(name); + + GCriticalSectionLock(&(const_cast<DjVuDocument *>(this)->ufiles_lock)); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) return f->file; + } + GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::PAGE_NUM, 0, + page_num, url, 0); + + // We're adding the record to the list before creating the DjVuFile + // because DjVuFile::init() will call request_data(), and the + // latter should be able to find the record. + // + // We also want to keep ufiles_lock to make sure that when + // request_data() is called, the record is still there + const_cast<DjVuDocument *>(this)->ufiles_list.append(ufile); + + GP<DjVuFile> file= + DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof); + ufile->file=file; + return file; + } else url=((DjVuFile *) (DjVuPort *) port)->get_url(); + } + } + + GP<DjVuFile> file=url_to_file(url, dont_create); + if (file) + pcaster->add_route(file, const_cast<DjVuDocument *>(this)); + return file; +} + +GURL +DjVuDocument::invent_url(const GUTF8String &name) const +{ + GUTF8String buffer; + buffer.format("djvufileurl://%p/%s", this, (const char *)name); + return GURL::UTF8(buffer); +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(const GUTF8String& id, bool dont_create) +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): ID='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + if (!id.length()) + return get_djvu_file(-1); + +// Integers are not supported, only ID's +// if (id.is_int()) +// return get_djvu_file(id.toInt(),dont_create); + + GURL url; + // I'm locking the flags because depending on what id_to_url() + // returns me, I'll be creating DjVuFile in different ways. + // And I don't want the situation to change between the moment I call + // id_to_url() and I actually create DjVuFile + { + GMonitorLock lock(&flags); + url=id_to_url(id); + if(url.is_empty() && !id.is_int()) + { + // If init is complete, we know for sure, that there is no such + // file with ID 'id' in the document. Otherwise we have to + // create a temporary file and wait for the init to finish + if (is_init_complete()) + return 0; + // Invent some dummy temporary URL. I don't care what it will + // be. I'll remember the ID and will generate the correct URL + // after I learn what the document is + url=invent_url(id); + DEBUG_MSG("Invented url='" << url << "'\n"); + + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) + return f->file; + } + GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::ID, id, 0, url, 0); + + // We're adding the record to the list before creating the DjVuFile + // because DjVuFile::init() will call request_data(), and the + // latter should be able to find the record. + // + // We also want to keep ufiles_lock to make sure that when + // request_data() is called, the record is still there + ufiles_list.append(ufile); + + GP<DjVuFile> file=DjVuFile::create(url,this,recover_errors,verbose_eof); + ufile->file=file; + return file; + } + } + + return get_djvu_file(url,dont_create); +} + +GP<DjVuFile> +DjVuDocument::get_djvu_file(const GURL& url, bool dont_create) +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvu_file(): URL='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (url.is_empty()) + return 0; + + const GP<DjVuFile> file(url_to_file(url, dont_create)); + + if (file) + get_portcaster()->add_route(file, this); + + return file; +} + +GP<DjVuImage> +DjVuDocument::get_page(int page_num, bool sync, DjVuPort * port) const +{ + check(); + DEBUG_MSG("DjVuDocument::get_page(): request for page " << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVuImage> dimg; + const GP<DjVuFile> file(get_djvu_file(page_num)); + if (file) + { + dimg=DjVuImage::create(file); + if (port) + DjVuPort::get_portcaster()->add_route(dimg, port); + + file->resume_decode(); + if (dimg && sync) + dimg->wait_for_complete_decode(); + } + return dimg; +} + +GP<DjVuImage> +DjVuDocument::get_page(const GUTF8String &id, bool sync, DjVuPort * port) +{ + check(); + DEBUG_MSG("DjVuDocument::get_page(): ID='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVuImage> dimg; + const GP<DjVuFile> file(get_djvu_file(id)); + if(file) + { + dimg=DjVuImage::create(file); + if (port) + DjVuPort::get_portcaster()->add_route(dimg, port); + + file->resume_decode(); + if (dimg && sync) + dimg->wait_for_complete_decode(); + } + return dimg; +} + +void +DjVuDocument::process_threqs(void) + // Will look thru threqs_list and try to fulfil every request +{ + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;) + { + GP<ThumbReq> req=threqs_list[pos]; + bool remove=false; + if (req->thumb_file) + { + G_TRY { + // There is supposed to be a file with thumbnails + if (req->thumb_file->is_data_present()) + { + // Cool, we can extract the thumbnail now + GP<ByteStream> str=req->thumb_file->get_init_data_pool()->get_stream(); + GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (!iff.get_chunk(chkid) || chkid!="FORM:THUM") + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + + for(int i=0;i<req->thumb_chunk;i++) + { + if (!iff.get_chunk(chkid)) + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + iff.close_chunk(); + } + if (!iff.get_chunk(chkid) || chkid!="TH44") + G_THROW( ERR_MSG("DjVuDocument.bad_thumb") ); + + // Copy the data + char buffer[1024]; + int length; + while((length=iff.read(buffer, 1024))) + req->data_pool->add_data(buffer, length); + req->data_pool->set_eof(); + + // Also add this file to cache so that we won't have + // to download it next time + add_to_cache(req->thumb_file); + + req->thumb_file=0; + req->image_file=0; + remove=true; + } + } G_CATCH(exc) { + GUTF8String msg= ERR_MSG("DjVuDocument.cant_extract") "\n"; + msg+=exc.get_cause(); + get_portcaster()->notify_error(this, msg); + // Switch this request to the "decoding" mode + req->image_file=get_djvu_file(req->page_num); + req->thumb_file=0; + req->data_pool->set_eof(); + remove=true; + } G_ENDCATCH; + } // if (req->thumb_file) + + if (req->image_file) + { + G_TRY { + // Decode the file if necessary. Or just used predecoded image. + GSafeFlags & file_flags=req->image_file->get_safe_flags(); + { + GMonitorLock lock(&file_flags); + if (!req->image_file->is_decoding()) + { + if (req->image_file->is_decode_ok()) + { + // We can generate it now + const GP<DjVuImage> dimg(DjVuImage::create(req->image_file)); + + dimg->wait_for_complete_decode(); + + int width = 160; + int height = 160; + + if( dimg->get_width() ) + width = dimg->get_width(); + if( dimg->get_height() ) + height = dimg->get_height(); + + GRect rect(0, 0, 160, height*160/width); + GP<GPixmap> pm=dimg->get_pixmap(rect, rect, thumb_gamma); + if (!pm) + { + GP<GBitmap> bm=dimg->get_bitmap(rect, rect, sizeof(int)); + if(bm) + pm=GPixmap::create(*bm); + else + pm = GPixmap::create(rect.height(), rect.width(), + &GPixel::WHITE); + } + + // Store and compress the pixmap + GP<IW44Image> iwpix=IW44Image::create_encode(*pm); + GP<ByteStream> gstr=ByteStream::create(); + IWEncoderParms parms; + parms.slices=97; + parms.bytes=0; + parms.decibels=0; + iwpix->encode_chunk(gstr, parms); + TArray<char> data=gstr->get_data(); + + req->data_pool->add_data((const char *) data, data.size()); + req->data_pool->set_eof(); + + req->thumb_file=0; + req->image_file=0; + remove=true; + } else if (req->image_file->is_decode_failed()) + { + // Unfortunately we cannot decode it + req->thumb_file=0; + req->image_file=0; + req->data_pool->set_eof(); + remove=true; + } else + { + req->image_file->start_decode(); + } + } + } + } G_CATCH(exc) { + GUTF8String msg="Failed to decode thumbnails:\n"; + msg+=exc.get_cause(); + get_portcaster()->notify_error(this, msg); + + // Get rid of this request + req->image_file=0; + req->thumb_file=0; + req->data_pool->set_eof(); + remove=true; + } G_ENDCATCH; + } + + if (remove) + { + GPosition this_pos=pos; + ++pos; + threqs_list.del(this_pos); + } else ++pos; + } +} + +GP<DjVuDocument::ThumbReq> +DjVuDocument::add_thumb_req(const GP<ThumbReq> & thumb_req) + // Will look through the list of pending requests for thumbnails + // and try to add the specified request. If a duplicate is found, + // it will be returned and the list will not be modified +{ + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;++pos) + { + GP<ThumbReq> req=threqs_list[pos]; + if (req->page_num==thumb_req->page_num) + return req; + } + threqs_list.append(thumb_req); + return thumb_req; +} + +GList<GUTF8String> +DjVuDocument::get_id_list(void) +{ + GList<GUTF8String> ids; + if (is_init_complete()) + { + if(djvm_dir) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + ids.append(files_list[pos]->get_load_name()); + } + }else + { + const int page_num=get_pages_num(); + for(int page=0;page<page_num;page++) + { + ids.append(page_to_url(page).fname()); + } + } + } + return ids; +} + +void +DjVuDocument::map_ids(GMap<GUTF8String,void *> &map) +{ + GList<GUTF8String> ids=get_id_list(); + for(GPosition pos=ids;pos;++pos) + { + map[ids[pos]]=0; + } +} + +GP<DataPool> +DjVuDocument::get_thumbnail(int page_num, bool dont_decode) +{ + DEBUG_MSG("DjVuDocument::get_thumbnail(): page_num=" << page_num << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!is_init_complete()) return 0; + + { + // See if we already have request for this thumbnail pending + GCriticalSectionLock lock(&threqs_lock); + for(GPosition pos=threqs_list;pos;++pos) + { + GP<ThumbReq> req=threqs_list[pos]; + if (req->page_num==page_num) + return req->data_pool; // That's it. Just return it. + } + } + + // No pending request for this page... Create one + GP<ThumbReq> thumb_req=new ThumbReq(page_num, DataPool::create()); + + // First try to find predecoded thumbnail + if (get_doc_type()==INDIRECT || get_doc_type()==BUNDLED) + { + // Predecoded thumbnails exist for new formats only + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + GP<DjVmDir::File> thumb_file; + int thumb_start=0; + int page_cnt=-1; + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> f=files_list[pos]; + if (f->is_thumbnails()) + { + thumb_file=f; + thumb_start=page_cnt+1; + } else if (f->is_page()) + { + page_cnt++; + } + if (page_cnt==page_num) break; + } + if (thumb_file) + { + // That's the file with the desired thumbnail image + thumb_req->thumb_file=get_djvu_file(thumb_file->get_load_name()); + thumb_req->thumb_chunk=page_num-thumb_start; + thumb_req=add_thumb_req(thumb_req); + process_threqs(); + return thumb_req->data_pool; + } + } + + // Apparently we're out of luck and need to decode the requested + // page (unless it's already done and if it's allowed) and render + // it into the thumbnail. If dont_decode is true, do not attempt + // to create this file (because this will result in a request for data) + GP<DjVuFile> file=get_djvu_file(page_num, dont_decode); + if (file) + { + thumb_req->image_file=file; + + // I'm locking the flags here to make sure, that DjVuFile will not + // change its state in between of the checks. + GSafeFlags & file_flags=file->get_safe_flags(); + { + GMonitorLock lock(&file_flags); + if (thumb_req->image_file->is_decode_ok() || !dont_decode) + { + // Just add it to the list and call process_threqs(). It + // will start decoding if necessary + thumb_req=add_thumb_req(thumb_req); + process_threqs(); + } else + { + // Nothing can be done return ZERO + thumb_req=0; + } + } + } else thumb_req=0; + + if (thumb_req) return thumb_req->data_pool; + else return 0; +} + +static void +add_to_cache(const GP<DjVuFile> & f, GMap<GURL, void *> & map, + DjVuFileCache * cache) +{ + GURL url=f->get_url(); + DEBUG_MSG("DjVuDocument::add_to_cache(): url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!map.contains(url)) + { + map[url]=0; + cache->add_file(f); + + GPList<DjVuFile> list; + for(GPosition pos=list;pos;++pos) + add_to_cache(list[pos], map, cache); + } +} + +void +DjVuDocument::add_to_cache(const GP<DjVuFile> & f) +{ + if (cache) + { + GMap<GURL, void *> map; + ::add_to_cache(f, map, cache); + } +} + +void +DjVuDocument::notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask) +{ + // Don't check here if the document is initialized or not. + // This function may be called when it's not. + // check(); + if (set_mask & DjVuFile::DECODE_OK) + { + set_file_aliases(source); + if (cache) add_to_cache((DjVuFile *) source); + if(!needs_compression_flag) + { + if(source->needs_compression()) + { + can_compress_flag=true; + needs_compression_flag=true; + }else if(source->can_compress()) + { + can_compress_flag=true; + } + } + process_threqs(); + } + + if (set_mask & DjVuFile::DATA_PRESENT) + process_threqs(); // May be we can extract thumbnails now +} + +GP<DjVuFile> +DjVuDocument::id_to_file(const DjVuPort * source, const GUTF8String &id) +{ + return (DjVuFile *) get_djvu_file(id); +} + +GP<DataPool> +DjVuDocument::request_data(const DjVuPort * source, const GURL & url) +{ + DEBUG_MSG("DjVuDocument::request_data(): seeing if we can do it\n"); + DEBUG_MAKE_INDENT(3); + + if (url==init_url) + return init_data_pool; + + check(); // Don't put it before 'init_data_pool' + + { + // See if there is a file in the "UnnamedFiles" list. + // If it's there, then create an empty DataPool and store its + // pointer in the list. The "init thread" will eventually + // do smth with it. + GCriticalSectionLock lock(&ufiles_lock); + for(GPosition pos=ufiles_list;pos;++pos) + { + GP<UnnamedFile> f=ufiles_list[pos]; + if (f->url==url) + { + DEBUG_MSG("Found tmp unnamed DjVuFile. Return empty DataPool\n"); + // Remember the DataPool. We will connect it to the + // actual data after the document structure becomes known + f->data_pool=DataPool::create(); + return f->data_pool; + } + } + } + + // Well, the url is not in the "UnnamedFiles" list, but it doesn't + // mean, that it's not "artificial". Stay alert! + GP<DataPool> data_pool; + if (flags & DOC_TYPE_KNOWN) + switch(doc_type) + { + case OLD_BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + DEBUG_MSG("The document is in OLD_BUNDLED format\n"); + if (url.base()!=init_url) + G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"+url.get_string()); + + GP<DjVmDir0::FileRec> file=djvm_dir0->get_file(url.fname()); + if (!file) + { + G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname()); + } + data_pool=DataPool::create(init_data_pool, file->offset, file->size); + } + break; + } + case BUNDLED: + { + if (flags & DOC_DIR_KNOWN) + { + DEBUG_MSG("The document is in new BUNDLED format\n"); + if (url.base()!=init_url) + { + G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t" + +url.get_string()); + } + + GP<DjVmDir::File> file=djvm_dir->id_to_file(url.fname()); + if (!file) + { + G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname()); + } + data_pool=DataPool::create(init_data_pool, file->offset, file->size); + } + break; + } + case SINGLE_PAGE: + case OLD_INDEXED: + case INDIRECT: + { + DEBUG_MSG("The document is in SINGLE_PAGE or OLD_INDEXED or INDIRECT format\n"); + if (flags & DOC_DIR_KNOWN) + if (doc_type==INDIRECT && !djvm_dir->id_to_file(url.fname())) + G_THROW( ERR_MSG("DjVuDocument.URL_outside2") "\t"+url.get_string()); + + if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + DEBUG_MSG("url=" << url << "\n"); + + data_pool=DataPool::create(url); + } + } + } + return data_pool; +} + + +static void +add_file_to_djvm(const GP<DjVuFile> & file, bool page, + DjVmDoc & doc, GMap<GURL, void *> & map) + // This function is used only for obsolete formats. + // For new formats there is no need to process files recursively. + // All information is already available from the DJVM chunk +{ + GURL url=file->get_url(); + + if (!map.contains(url)) + { + map[url]=0; + + if (file->get_chunks_number()>0 && !file->contains_chunk("NDIR")) + { + // Get the data and unlink any file containing NDIR chunk. + // Yes. We're lazy. We don't check if those files contain + // anything else. + GPosition pos; + GPList<DjVuFile> files_list=file->get_included_files(false); + GP<DataPool> data=file->get_djvu_data(false); + for(pos=files_list;pos;++pos) + { + GP<DjVuFile> f=files_list[pos]; + if (f->contains_chunk("NDIR")) + data=DjVuFile::unlink_file(data, f->get_url().fname()); + } + + // Finally add it to the document + GUTF8String name=file->get_url().fname(); + GP<DjVmDir::File> file_rec=DjVmDir::File::create( + name, name, name, + page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE ); + doc.insert_file(file_rec, data, -1); + + // And repeat for all included files + for(pos=files_list;pos;++pos) + add_file_to_djvm(files_list[pos], false, doc, map); + } + } +} + +static void +add_file_to_djvm(const GP<DjVuFile> & file, bool page, + DjVmDoc & doc, GMap<GURL, void *> & map, + bool &needs_compression_flag, bool &can_compress_flag ) +{ + if(!needs_compression_flag) + { + if(file->needs_compression()) + { + can_compress_flag=true; + needs_compression_flag=true; + }else if(file->can_compress()) + { + can_compress_flag=true; + } + } + add_file_to_djvm(file,page,doc,map); +} + +static void +local_get_url_names(DjVuFile * f,const GMap<GURL, void *> & map,GMap<GURL,void *> &tmpmap) +{ + GURL url=f->get_url(); + if (!map.contains(url) && !tmpmap.contains(url)) + { + tmpmap[url]=0; + f->process_incl_chunks(); + GPList<DjVuFile> files_list=f->get_included_files(false); + for(GPosition pos=files_list;pos;++pos) + local_get_url_names(files_list[pos], map, tmpmap); + } +} + +static void +local_get_url_names(DjVuFile * f, GMap<GURL, void *> & map) +{ + GMap<GURL,void *> tmpmap; + local_get_url_names(f,map,tmpmap); + for(GPosition pos=tmpmap;pos;++pos) + map[tmpmap.key(pos)]=0; +} + +GList<GURL> +DjVuDocument::get_url_names(void) +{ + check(); + + GCriticalSectionLock lock(&url_names_lock); + if(has_url_names) + return url_names; + + GMap<GURL, void *> map; + int i; + if (doc_type==BUNDLED || doc_type==INDIRECT) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GURL url=id_to_url(files_list[pos]->get_load_name()); + map[url]=0; + } + }else + { + int pages_num=get_pages_num(); + for(i=0;i<pages_num;i++) + { + G_TRY + { + local_get_url_names(get_djvu_file(i), map); + } + G_CATCH(ex) + { + // Why is this try/catch block here? + G_TRY { + get_portcaster()->notify_error(this, ex.get_cause()); + GUTF8String emsg = ERR_MSG("DjVuDocument.exclude_page") "\t" + (i+1); + get_portcaster()->notify_error(this, emsg); + } + G_CATCH_ALL + { + G_RETHROW; + } + G_ENDCATCH; + } + G_ENDCATCH; + } + } + for(GPosition j=map;j;++j) + { + if (map.key(j).is_local_file_url()) + { + url_names.append(map.key(j)); + } + } + has_url_names=true; + return url_names; +} + +GP<DjVmDoc> +DjVuDocument::get_djvm_doc() + // This function may block for data +{ + check(); + DEBUG_MSG("DjVuDocument::get_djvm_doc(): creating the DjVmDoc\n"); + DEBUG_MAKE_INDENT(3); + + if (!is_init_complete()) + G_THROW( ERR_MSG("DjVuDocument.init_not_done") ); + + GP<DjVmDoc> doc=DjVmDoc::create(); + + if (doc_type==BUNDLED || doc_type==INDIRECT) + { + GPList<DjVmDir::File> files_list=djvm_dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> f=new DjVmDir::File(*files_list[pos]); + GP<DjVuFile> file=url_to_file(id_to_url(f->get_load_name())); + GP<DataPool> data; + if (file->is_modified()) + data=file->get_djvu_data(false); + else + data=file->get_init_data_pool(); + doc->insert_file(f, data); + } + if (djvm_nav) + doc->set_djvm_nav(djvm_nav); + } + else if (doc_type==SINGLE_PAGE) + { + DEBUG_MSG("Creating: djvm for a single page document.\n"); + GMap<GURL, void *> map_add; + GP<DjVuFile> file=get_djvu_file(0); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + else + { + DEBUG_MSG("Converting: the document is in an old format.\n"); + GMap<GURL, void *> map_add; + if(recover_errors == ABORT) + { + for(int page_num=0;page_num<ndir->get_pages_num();page_num++) + { + GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num)); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + } + else + { + for(int page_num=0;page_num<ndir->get_pages_num();page_num++) + { + G_TRY + { + GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num)); + add_file_to_djvm(file, true, *doc, map_add, + needs_compression_flag,can_compress_flag); + } + G_CATCH(ex) + { + G_TRY { + get_portcaster()->notify_error(this, ex.get_cause()); + GUTF8String emsg = ERR_MSG("DjVuDocument.skip_page") "\t" + + (page_num+1); + get_portcaster()->notify_error(this, emsg); + } + G_CATCH_ALL + { + G_RETHROW; + } + G_ENDCATCH; + } + G_ENDCATCH; + } + } + } + return doc; +} + +void +DjVuDocument::write( const GP<ByteStream> &gstr, + const GMap<GUTF8String,void *> &reserved) +{ + DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n"); + DEBUG_MAKE_INDENT(3); + get_djvm_doc()->write(gstr,reserved); +} + +void +DjVuDocument::write(const GP<ByteStream> &gstr, bool force_djvm) +{ + DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVmDoc> doc=get_djvm_doc(); + GP<DjVmDir> dir=doc->get_djvm_dir(); + if (force_djvm || dir->get_files_num()>1) + { + doc->write(gstr); + }else + { + GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false); + GP<DataPool> pool=doc->get_data(files_list[files_list]->get_load_name()); + GP<ByteStream> pool_str=pool->get_stream(); + ByteStream &str=*gstr; + str.writall(octets,4); + str.copy(*pool_str); + } +} + +void +DjVuDocument::expand(const GURL &codebase, const GUTF8String &idx_name) +{ + DEBUG_MSG("DjVuDocument::expand(): codebase='" << codebase << "'\n"); + DEBUG_MAKE_INDENT(3); + + GP<DjVmDoc> doc=get_djvm_doc(); + doc->expand(codebase, idx_name); +} + +void +DjVuDocument::save_as(const GURL &where, const bool bundled) +{ + DEBUG_MSG("DjVuDocument::save_as(): where='" << where << + "', bundled=" << bundled << "\n"); + DEBUG_MAKE_INDENT(3); + + if (needs_compression()) + { + if(!djvu_compress_codec) + { + G_THROW( ERR_MSG("DjVuDocument.comp_codec") ); + } + GP<ByteStream> gmbs=ByteStream::create(); + write(gmbs); + ByteStream &mbs=*gmbs; + mbs.flush(); + mbs.seek(0,SEEK_SET); + (*djvu_compress_codec)(gmbs,where,bundled); + }else if (bundled) + { + DataPool::load_file(where); + write(ByteStream::create(where, "wb")); + } else + { + expand(where.base(), where.fname()); + } +} + +static const char prolog[]="<?xml version=\"1.0\" ?>\n<!DOCTYPE DjVuXML PUBLIC \"-//W3C//DTD DjVuXML 1.1//EN\" \"pubtext/DjVuXML-s.dtd\">\n<DjVuXML>\n<HEAD>"; +static const char start_xml[]="</HEAD>\n<BODY>\n"; +static const char end_xml[]="</BODY>\n</DjVuXML>\n"; + +void +DjVuDocument::writeDjVuXML(const GP<ByteStream> &gstr_out,int flags) const +{ + ByteStream &str_out=*gstr_out; + str_out.writestring( + prolog+get_init_url().get_string().toEscaped()+start_xml); + const int pages=wait_get_pages_num(); + for(int page_num=0;page_num<pages;++page_num) + { + const GP<DjVuImage> dimg(get_page(page_num,true)); + if(!dimg) + { + G_THROW( ERR_MSG("DjVuToText.decode_failed") ); + } + dimg->writeXML(str_out,get_init_url(),flags); + } + str_out.writestring(GUTF8String(end_xml)); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h new file mode 100644 index 00000000..418d0814 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDocument.h @@ -0,0 +1,1071 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDocument.h,v 1.10 2005/05/25 20:24:52 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDOCUMENT_H +#define _DJVUDOCUMENT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DjVmDoc; +class DjVmDir; +class DjVmDir0; +class DjVmNav; +class DjVuImage; +class DjVuFile; +class DjVuFileCache; +class DjVuNavDir; +class ByteStream; + +/** @name DjVuDocument.h + Files #"DjVuDocument.h"# and #"DjVuDocument.cpp"# contain implementation + of the \Ref{DjVuDocument} class - the ideal tool for opening, decoding + and saving DjVu single page and multi page documents. + + @memo DjVu document class. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVuDocument.h,v 1.10 2005/05/25 20:24:52 leonb Exp $# +*/ + +//@{ + +/** #DjVuDocument# provides convenient interface for opening, decoding + and saving back DjVu documents in single page and multi page formats. + + {\bf Input formats} + It can read multi page DjVu documents in either of the 4 formats: 2 + obsolete ({\em old bundled} and {\em old indexed}) and two new + ({\em new bundled} and {\em new indirect}). + + {\bf Output formats} + To encourage users to switch to the new formats, the #DjVuDocument# can + save documents back only in the new formats: {\em bundled} and + {\em indirect}. + + {\bf Conversion.} Since #DjVuDocument# can open DjVu documents in + an obsolete format and save it in any of the two new formats + ({\em new bundled} and {\em new indirect}), this class can be used for + conversion from obsolete formats to the new ones. Although it can also + do conversion between the new two formats, it's not the best way to + do it. Please refer to \Ref{DjVmDoc} for details. + + {\bf Decoding.} #DjVuDocument# provides convenient interface for obtaining + \Ref{DjVuImage} corresponding to any page of the document. It uses + \Ref{DjVuFileCache} to do caching thus avoiding unnecessary multiple decoding of + the same page. The real decoding though is accomplished by \Ref{DjVuFile}. + + {\bf Messenging.} Being derived from \Ref{DjVuPort}, #DjVuDocument# + takes an active part in exchanging messages (requests and notifications) + between different parties involved in decoding. It reports (relays) + errors, progress information and even handles some requests for data (when + these requests deal with local files). + + Typical usage of #DjVuDocument# class in a threadless command line + program would be the following: + \begin{verbatim} + static const char file_name[]="/tmp/document.djvu"; + GP<DjVuDocument> doc=DjVuDocument::create_wait(file_name); + const int pages=doc->get_pages_num(); + for(int page=0;page<pages;page++) + { + GP<DjVuImage> dimg=doc->get_page(page); + // Do something + }; + \end{verbatim} + + {\bf Comments for the code above} + \begin{enumerate} + \item Since the document is assumed to be stored on the hard drive, + we don't have to cope with \Ref{DjVuPort}s and can pass + #ZERO# pointer to the \Ref{init}() function. #DjVuDocument# + can access local data itself. In the case of a plugin though, + one would have to implement his own \Ref{DjVuPort}, which + would handle requests for data arising when the document + is being decoded. + \item In a threaded program instead of calling the \Ref{init}() + function one can call \Ref{start_init}() and \Ref{stop_init}() + to initiate and interrupt initialization carried out in + another thread. This possibility of initializing the document + in another thread has been added specially for the plugin + because the initialization itself requires data, which is + not immediately available in the plugin. Thus, to prevent the + main thread from blocking, we perform initialization in a + separate thread. To check if the class is completely and + successfully initialized, use \Ref{is_init_ok}(). To see if + there was an error, use \Ref{is_init_failed}(). To + know when initialization is over (whether successfully or not), + use \Ref{is_init_complete}(). To wait for this to happen use + \Ref{wait_for_complete_init}(). Once again, all these things are + not required for single-threaded program. + + Another difference between single-threaded and multi-threaded + environments is that in a single-threaded program, the image is + fully decoded before it's returned. In a multi-threaded + application decoding starts in a separate thread, and the pointer + to the \Ref{DjVuImage} being decoded is returned immediately. + This has been done to enable progressive redisplay + in the DjVu plugin. Use communication mechanism provided by + \Ref{DjVuPort} and \Ref{DjVuPortcaster} to learn about progress + of decoding. Or try #dimg->wait_for_complete_decode()# to wait + until the decoding ends. + \item See Also: \Ref{DjVuFile}, \Ref{DjVuImage}, \Ref{GOS}. + \end{enumerate} + + {\bf Initialization} + As mentioned above, the #DjVuDocument# can go through several stages + of initialization. The functionality is gradually added while it passes + one stage after another: + \begin{enumerate} + \item First of all, immediately after the object is created \Ref{init}() + or \Ref{start_init}() functions must be called. {\bf Nothing} + will work until this is done. \Ref{init}() function will not + return until the initialization is complete. You need to make + sure, that enough data is available. {\bf Do not call \Ref{init}() + in the plugin}. \Ref{start_init}() will start initialization + in another thread. Use \Ref{stop_init}() to interrupt it. + Use \Ref{is_init_complete}() to check the initialization progress. + Use \Ref{wait_for_complete_init}() to wait for init to finish. + \item The first thing the initializing code learns about the document + is its type (#BUNDLED#, #INDIRECT#, #OLD_BUNDLED# or #OLD_INDEXED#). + As soon as it happens, document flags are changed and + #notify_doc_flags_changed()# request is sent through the + communication mechanism provided by \Ref{DjVuPortcaster}. + \item After the document type becomes known, the initializing code + proceeds with learning the document structure. Gradually the + flags are updated with values: + \begin{itemize} + \item #DOC_DIR_KNOWN#: Contents of the document became known. + This is meaningful for #BUNDLED#, #OLD_BUNDLED# and + #INDIRECT# documents only. + \item #DOC_NDIR_KNOWN#: Contents of the document navigation + directory became known. This is meaningful for old-style + documents (#OLD_BUNDLED# and #OLD_INDEXED#) only + \item #DOC_INIT_OK# or #DOC_INIT_FAILED#: + The initializating code finished. + \end{itemize} + \end{enumerate} */ + +class DjVuDocument : public DjVuPort +{ +public: + /** Flags describing the document initialization state. + \begin{itemize} + \item #DOC_TYPE_KNOWN#: The type of the document has been learnt. + \item #DOC_DIR_KNOWN#: Contents of the document became known. + This is meaningful for #BUNDLED#, #OLD_BUNDLED# and + #INDIRECT# documents only. + \item #DOC_NDIR_KNOWN#: Contents of the document navigation + directory became known. This is meaningful for old-style + documents (#OLD_BUNDLED# and #OLD_INDEXED#) only + \item #DOC_INIT_OK#: The initialization has completed successfully. + \item #DOC_INIT_FAILED#: The initialization failed. + \end{itemize} */ + enum DOC_FLAGS { DOC_TYPE_KNOWN=1, DOC_DIR_KNOWN=2, + DOC_NDIR_KNOWN=4, DOC_INIT_OK=8, + DOC_INIT_FAILED=16 }; + /** Specifies the format of #DjVuDocument#. There are currently 4 DjVu + multipage formats recognized by the library. Two of them are obsolete + and should not be used. + \begin{enumerate} + \item #OLD_BUNDLED# - Obsolete bundled format + \item #OLD_INDEXED# - Obsolete multipage format where every page + is stored in a separate file and "includes" (by means + of an #INCL# chunk) the file with the document directory. + \item #SINGLE_PAGE# - Single page document. Basically a file + with either #FORM:DJVU# or #FORM:IW44# and no multipage + information. For example, #OLD_INDEXED# documents with + document directory do not qualify even if they contain only + one page. + \item #BUNDLED# - Currently supported bundled format + \item #INDIRECT# - Currently supported "expanded" format, where + every page and component is stored in a separate file. There + is also a {\em top-level} file with the document directory. + \end{enumerate} */ + enum DOC_TYPE { OLD_BUNDLED=1, OLD_INDEXED, BUNDLED, INDIRECT, + SINGLE_PAGE, UNKNOWN_TYPE }; + enum THREAD_FLAGS { STARTED=1, FINISHED=2 }; + +protected: + /** Default creator. Please call functions \Ref{init}() or + \Ref{start_init}() before you start working with the #DjVuDocument#. + */ + DjVuDocument(void); +public: + + /// Virtual Destructor + virtual ~DjVuDocument(void); + + /** Initializes the #DjVuDocument# object using an existing document. + This function should be called once after creating the object. + The #url# should point to the real data, and the creator of the + document should be ready to return this data to the document + if it's not stored locally (in which case #DjVuDocument# can + access it itself). + + {\bf Initializing thread} + In a single-threaded application, the #start_init()# function performs + the complete initialization of the #DjVuDocument# before it returns. + In a multi-threaded application, though, it initializes some internal + variables, requests data for the document and starts a new + {\em initializing} thread, which is responsible for determining the + document type and structure and completing the initialization + process. This additional complication is justified in the case of + the DjVu plugin because performing initialization requires data and + in the plugin the data can be supplied by the main thread only. + Thus, if the initialization was completed by the main thread, the + plugin would run out of data and block. + + {\bf Stages of initialization} + Immediately after the #start_init()# function terminates, the + #DjVuDocument# object is ready for use. Its functionality will + not be complete (until the initializing thread finishes), but + the object is still very useful. Such functions as \Ref{get_page}() + or \Ref{get_djvu_file}() or \Ref{id_to_url}() may be called + before the initializing thread completes. This allows the DjVu + plugin start decoding as soon as possible without waiting for + all data to arrive. + + To query the current stage of initialization you can use + \Ref{get_doc_flags}() function or listen to the + #notify_doc_flags_changed()# notifications distributed with the help + of \Ref{DjVuPortcaster}. To wait for the initialization to + complete use \Ref{wait_for_complete_init}(). To stop initialization + call \Ref{stop_init}(). + + {\bf Querying data} + The query for data is done using the communication mechanism + provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. If #port# + is not #ZERO#, then the request for data will be forwarded to it. + If it {\bf is} #ZERO# then #DjVuDocument# will create an internal + instance of \Ref{DjVuSimplePort} and will use it to access local + files and report errors to #stderr#. In short, if the document + file is stored on the local hard disk, and you're OK about reporting + errors to #stderr#, you may pass #ZERO# pointer to \Ref{DjVuPort} + as #DjVuDocument# can take care of this situation by itself. + + {\bf The URL} + Depending on the document type the #url# should point to: + \begin{itemize} + \item {\bf Old bundled} and {\bf New bundled} formats: to the + document itself. + \item {\bf Old indexed} format: to any page of the document. + \item {\bf New indirect} format: to the top-level file of the + document. If (like in the {\em old indexed} format) you + point the #url# to a page, the page {\em will} be decoded, + but it will {\em not} be recognized to be part of the + document. + \end{itemize} + + @param url The URL pointing to the document. If the document is + in a {\em bundled} format then the URL should point to it. + If the document is in the {\em old indexed} format then + URL may point to any page of this document. For {\em new + indirect} format the URL should point to the top-level + file of the document. + @param port If not #ZERO#, all requests and notifications will + be sent to it. Otherwise #DjVuDocument# will create an internal + instance of \Ref{DjVuSimplePort} for these purposes. + It's OK to make it #ZERO# if you're writing a command line + tool, which should work with files on the hard disk only + because #DjVuDocument# can access such files itself. + @param cache It's used to cache decoded \Ref{DjVuFile}s and + is actually useful in the plugin only. */ + void start_init(const GURL & url, GP<DjVuPort> port=0, + DjVuFileCache * cache=0); + + /** This creates a DjVuDocument without initializing it. */ + static GP<DjVuDocument> create_noinit(void) {return new DjVuDocument;} + + /** Create a version of DjVuDocument which has finished initializing. */ + static GP<DjVuDocument> create_wait( + const GURL &url, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + const GURL &url, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + GP<DataPool> pool, GP<DjVuPort> xport=0, DjVuFileCache * const xcache=0); + + /** Create a version of DjVuDocument which has begun initializing. */ + static GP<DjVuDocument> create( + const GP<ByteStream> &bs, GP<DjVuPort> xport=0, + DjVuFileCache * const xcache=0); + + /** Call this function when you don't need the #DjVuDocument# any more. + In a multi-threaded environment it will stop initialization + thread, if it is currently running. {\bf You will not be able + to start the initialization again. Thus, after calling this + function the document should not be used any more}. */ + void stop_init(void); + + /** Initializes the document. + + Contrary to \Ref{start_init}(), which just starts the initialization + thread in a multi-threaded environment, this function does not + return until the initialization completes (either successfully or + not). Basically, it calls \Ref{start_init}() and then + \Ref{wait_for_complete_init}(). + */ + void init(const GURL & url, GP<DjVuPort> port=0, + DjVuFileCache * cache=0); + + /** Returns #TRUE# if the initialization thread finished (does not + matter successfully or not). As soon as it happens, the document + becomes completely initialized and its every function should work + properly. Please refer to the description of \Ref{init}() function + and of the #DjVuDocument# class to learn about the initializing + stages. + + To wait for the initialization to complete use + \Ref{wait_for_complete_init}() function. + + To query the initialization stage use \Ref{get_flags}() function. + + To learn whether initialization was successful or not, + use \Ref{is_init_ok}() and \Ref{is_init_failed}(). + + {\bf Note:} In a single threaded application the initialization + completes before the \Ref{init}() function returns. */ + bool is_init_complete(void) const; + + /** Returns #TRUE# is the initialization thread finished successfully. + + See \Ref{is_init_complete}() and \Ref{wait_for_complete_init}() + for more details. */ + bool is_init_ok(void) const; + /** Forces compression with the next save_as function. */ + void set_needs_compression(void); + /** Returns #TRUE# if there are uncompressed pages in this document. */ + bool needs_compression(void) const; + /** Returns #TRUE# if this file must be renamed before saving. */ + bool needs_rename(void) const; + /** Returns #TRUE# if this file must be renamed before saving. */ + bool can_compress(void) const; + + /** Returns #TRUE# is the initialization thread failed. + + See \Ref{is_init_complete}() and \Ref{wait_for_complete_init}() + for more details. */ + bool is_init_failed(void) const; + + /** If the document has already learnt its type, the function will + returns it: #DjVuDocument::OLD_BUNDLED# or + #DjVuDocument::OLD_INDEXED# or #DjVuDocument::SINGLE_PAGE# or + #DjVuDocument:BUNDLED# or #DjVuDocument::INDIRECT#. The first + two formats are obsolete. Otherwise (if the type is unknown yet), + #UNKNOWN_TYPE# will be returned. + + {\bf Note:} To check the stage of the document initialization + use \Ref{get_flags}() or \Ref{is_init_complete}() functions. To + wait for the initialization to complete use \Ref{wait_for_complete_init}(). + For single threaded applications the initialization completes + before the \Ref{init}() function returns. */ + int get_doc_type(void) const; + + /** Returns the document flags. The flags describe the degree in which + the #DjVuDocument# object is initialized. Every time the flags + are changed, a #notify_doc_flags_changed()# notification is + distributed using the \Ref{DjVuPortcaster} communication + mechanism. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. */ + long get_doc_flags(void) const; + + /** Returns #TRUE# if the document is in bundled format (either in + #DjVuDocument::OLD_BUNDLED# or #DjVuDocument::BUNDLED# formats). */ + bool is_bundled(void) const; + + /// Returns the URL passed to the \Ref{init}() function + GURL get_init_url(void) const; + + /// Returns a listing of id's used by this document. + GList<GUTF8String> get_id_list(void); + + /// Fill the id's into a GMap. + void map_ids( GMap<GUTF8String,void *> &map); + + /** Returns data corresponding to the URL passed to the \Ref{init}() + function. + + {\bf Note:} The pointer returned is guaranteed to be non-#ZERO# + only after the #DjVuDocument# learns its type (passes through + the first stage of initialization process). Please refer to + \Ref{init}() for details. */ + GP<DataPool> get_init_data_pool(void) const; + + /** @name Accessing pages */ + //@{ + /** Returns the number of pages in the document. If there is still + insufficient information about the document structure (initialization + has not finished yet), #1# will be returned. Please refer to + \Ref{init}() for details. */ + int get_pages_num(void) const; + + /** Translates the page number to the full URL of the page. This URL + is "artificial" for the {\em bundled} formats and is obtained + by appending the page name to the document's URL honoring possible + #;# and #?# in it. Negative page number has a special meaning for + #OLD_INDEXED# documents: it points to the URL, which the + #DjVuDocument# has been initialized with. For other formats this + is the same as page #0#. + + The function tries it best to map the page number to the URL. + Although, if the document structure has not been fully discovered + yet, an empty URL will be returned. Use \Ref{wait_for_complete_init}() + to wait until the document initialization completes. Refer to + \Ref{init}() for details. + + Depending on the document format, the function assumes, that there + is enough information to complete the request when: + \begin{itemize} + \item #OLD_INDEXED#: If #page_num<0#, #DOC_TYPE_KNOWN# flag must + be set. Otherwise #DOC_NDIR_KNOWN# must be set. + \item #OLD_BUNDLED#: If #page_num=0#, #DOC_DIR_KNOWN# flag must + be set. Otherwise #DOC_NDIR_KNOWN# flag must be set. + \item #INDIRECT# and #BUNDLED#: #DOC_DIR_KNOWN# flag must be set. + \end{itemize} */ + GURL page_to_url(int page_num) const; + /// Tranlate the page number to id... + GUTF8String page_to_id(int page_num) const + { return url_to_id(page_to_url(page_num)); } + /** Translates the page URL back to page number. Returns #-1# if the + page is not in the document or the document's structure + has not been learnt yet. + + Depending on the document format, the function starts working + properly as soon as: + \begin{itemize} + \item #OLD_INDEXED# and #OLD_BUNDLED# and #SINGLE_PAGE#: + #DOC_NDIR_KNOWN# is set + \item #INDIRECT# and #BUNDLED#: #DOC_DIR_KNOWN# is set. + \end{itemize} */ + int url_to_page(const GURL & url) const; + /// Map the specified url to it's id. + GUTF8String url_to_id(const GURL &url) const + { return url.fname(); } + + /** Translates the textual ID to the complete URL if possible. + + Depending on the document format the translation is done in the + following way: + \begin{itemize} + \item For #BUNDLED# and #INDIRECT# documents the function + scans the \Ref{DjVmDir} (the document directory) and + matches the ID against: + \begin{enumerate} + \item File ID from the \Ref{DjVmDir} + \item File name from the \Ref{DjVmDir} + \item File title from the \Ref{DjVmDir} + \end{enumerate} + Then for #BUNDLED# document the URL is obtained by + appending the #name# of the found file to the document's + URL. + + For #INDIRECT# documents the URL is obtained by + appending the #name# of the found file to the URL of + the directory containing the document. + \item For #OLD_BUNDLED# documents the function compares the ID + with internal name of every file inside the bundle and + composes an artificial URL by appending the file name to + the document's URL. + \item For #OLD_INDEXED# or #SINGLE_PAGE# documents the function + composes the URL by appending the ID to the URL of the + directory containing the document. + \end{itemize} + + If information obtained by the initialization thread is not + sufficient yet, the #id_to_url()# may return an empty URL. + Depending on the document type, the information is sufficient when + \begin{itemize} + \item #BUNDLED# and #INDIRECT#: #DOC_DIR_KNOWN# flag is set. + \item #OLD_BUNDLED# and #OLD_INDEXED# and #SINGLE_PAGE#: + #DOC_TYPE_KNOWN# flag is set. + \end{itemize} */ + GURL id_to_url(const GUTF8String &id) const; + /// Find out which page this id is... + int id_to_page(const GUTF8String &id) const + { return url_to_page(id_to_url(id)); } + + /** Returns \Ref{GP} pointer to \Ref{DjVuImage} corresponding to page + #page_num#. If caching is enabled, and there is a {\em fully decoded} + \Ref{DjVuFile} in the cache, the image will be reused and will + be returned fully decoded. Otherwise, if multi-threaded behavior + is allowed, and #sync# is set to #FALSE#, the decoding will be + started in a separate thread, which enables to do progressive + redisplay. Thus, in this case the image returned may be partially + decoded. + + Negative #page_num# has a special meaning for the {\em old indexed} + multipage documents: the #DjVuDocument# will start decoding of the + URL with which it has been initialized. For other formats page + #-1# is the same as page #0#. + + #DjVuDocument# can also connect the created page to the specified + #port# {\em before starting decoding}. This option will allow + the future owner of \Ref{DjVuImage} to receive all messages and + requests generated during its decoding. + + If this function is called before the document's structure becomes + known (the initialization process completes), the \Ref{DjVuFile}, + which the returned image will be attached to, will be assigned a + temporary artificial URL, which will be corrected as soon as enough + information becomes available. The trick prevents the main thread + from blocking and in some cases helps to start decoding earlier. + The URL is corrected and decoding will start as soon as + #DjVuDocument# passes some given stages of initialization and + \Ref{page_to_url}(), \Ref{id_to_url}() functions start working + properly. Please look through their description for details. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. + + @param page_num Number of the page to be decoded + @param sync When set to #TRUE# the function will not return + until the page is completely decoded. Otherwise, + in a multi-threaded program, this function will + start decoding in a new thread and will return + a partially decoded image. Refer to + \Ref{DjVuImage::wait_for_complete_decode}() and + \Ref{DjVuFile::is_decode_ok}(). + @param port A pointer to \Ref{DjVuPort}, that the created image + will be connected to. */ + GP<DjVuImage> get_page(int page_num, bool sync=true, DjVuPort * port=0) const; + GP<DjVuImage> get_page(int page_num, bool sync=true, DjVuPort * port=0) + { return const_cast<const DjVuDocument *>(this)->get_page(page_num,sync,port); } + + /** Returns \Ref{GP} pointer to \Ref{DjVuImage} corresponding to the + specified ID. This function behaves exactly as the #get_page()# + function above. The only thing worth mentioning here is how the #ID# + parameter is treated. + + First of all the function checks, if the ID contains a number. + If so, it just calls the #get_page()# function above. If ID is + #ZERO# or just empty, page number #-1# is assumed. Otherwise + the ID is translated to the URL using \Ref{id_to_url}(). */ + GP<DjVuImage> get_page(const GUTF8String &id, bool sync=true, DjVuPort * port=0); + + /** Returns \Ref{DjVuFile} corresponding to the specified page. + Normally it translates the page number to the URL using + \Ref{page_to_url}() and then creates \Ref{DjVuFile} initializing + it with data from the URL. + + The behavior becomes different, though in the case when the + document structure is unknown at the moment this function is called. + In this situations it invents a temporary URL, creates a + \Ref{DjVuFile}, initializes it with this URL and returns + immediately. The caller may start decoding the file right away + (if necessary). The decoding will block but will automatically + continue as soon as enough information is collected about the + document. This trick should be quite transparent to the user and + helps to prevent the main thread from blocking. The decoding will + unblock and this function will stop using this "trick" as soon + as #DjVuDocument# passes some given stages of initialization and + \Ref{page_to_url}(), \Ref{id_to_url}() functions start working + properly. + + If #dont_create# is #FALSE# the function will return the file + only if it already exists. + + {\bf Note:} To wait for the initialization to complete use + \Ref{wait_for_complete_init}(). For single threaded applications + the initialization completes before the \Ref{init}() function + returns. */ + GP<DjVuFile> get_djvu_file(int page_num, bool dont_create=false) const; + GP<DjVuFile> get_djvu_file(int page_num, bool dont_create=false) + { return const_cast<const DjVuDocument *>(this)->get_djvu_file(page_num,dont_create); } + + + /** Returns \Ref{DjVuFile} corresponding to the specified ID. + This function behaves exactly as the #get_djvu_file()# function + above. The only thing worth mentioning here is how the #ID# + parameter is treated. + + First off, \Ref{id_to_url}() is called. If not successfull, + the function checks, if the ID contains a number. + If so, it just calls the #get_djvu_file()# function above. If ID is + #ZERO# or just empty, page number #-1# is assumed. + + If #dont_create# is #FALSE# the function will return the file + only if it already exists. */ + GP<DjVuFile> get_djvu_file(const GUTF8String &id, bool dont_create=false); + GP<DjVuFile> get_djvu_file(const GURL &url, bool dont_create=false); + /** Returns a \Ref{DataPool} containing one chunk #TH44# with + the encoded thumbnail for the specified page. The function + first looks for thumbnails enclosed into the document and if + it fails to find one, it decodes the required page and creates + the thumbnail on the fly (unless #dont_decode# is true). + + {\bf Note:} It may happen that the returned \Ref{DataPool} will + not contain all the data you need. In this case you will need + to install a trigger into the \Ref{DataPool} to learn when the + data actually arrives. */ + virtual GP<DataPool> get_thumbnail(int page_num, bool dont_decode); + /* Will return gamma correction, which was used when creating + thumbnail images. If you need other gamma correction, you will + need to correct the thumbnails again. */ + float get_thumbnails_gamma(void) const; + //@} + + /** Waits until the document initialization process finishes. + It can finish either successfully or not. Use \Ref{is_init_ok}() + and \Ref{is_init_failed}() to learn the result code. + + As described in \Ref{start_init}(), for multi-threaded applications the + initialization is carried out in parallel with the main thread. + This function blocks the calling thread until the initializing + thread reads enough data, receives information about the document + format and exits. This function returns #true# if the + initialization is successful. You can use \Ref{get_flags}() or + \Ref{is_init_complete}() to check more precisely the degree of + initialization. Use \Ref{stop_init}() to interrupt initialization. */ + bool wait_for_complete_init(void); + + /** Wait until we known the number of pages and return. */ + int wait_get_pages_num(void) const; + + /// Returns cache being used. + DjVuFileCache * get_cache(void) const; + + /** @name Saving document to disk */ + //@{ + /** Returns pointer to the \Ref{DjVmDoc} class, which can save the + document contents on the hard disk in one of the two new formats: + {\em bundled} and {\em indirect}. You may also want to look + at \Ref{write}() and \Ref{expand}() if you are interested in + how to save the document. + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + GP<DjVmDoc> get_djvm_doc(void); + /** Saves the document in the {\em new bundled} format. All the data + is "bundled" into one file and this file is written into the + passed stream. + + If #force_djvm# is #TRUE# then even one page documents will be + saved in the #DJVM BUNDLED# format (inside a #FORM:DJVM#); + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. */ + virtual void write(const GP<ByteStream> &str, bool force_djvm=false); + /** Always save as bundled, renaming any files conflicting with the + the names in the supplied GMap. */ + virtual void write(const GP<ByteStream> &str, + const GMap<GUTF8String,void *> &reserved); + /** Saves the document in the {\em new indirect} format when every + page and component are stored in separate files. This format + is ideal for web publishing because it allows direct access to + any page and component. In addition to it, a top-level file + containing the list of all components will be created. To view + the document later in the plugin or in the viewer one should + load the top-level file. + + {\bf Plugin Warning}. This function will read contents of the whole + document. Thus, if you call it from the main thread (the thread, + which transfers data from Netscape), the plugin will block. + + @param codebase - Name of the directory which the document should + be expanded into. + @param idx_name - Name of the top-level file containing the document + directory (basically, list of all files composing the document). + */ + void expand(const GURL &codebase, const GUTF8String &idx_name); + /** This function can be used instead of \Ref{write}() and \Ref{expand}(). + It allows to save the document either in the new #BUNDLED# format + or in the new #INDIRECT# format depending on the value of parameter + #bundled#. + + Depending on the document's type, the meaning of #where# is: + \begin{itemize} + \item For #BUNDLED# documents this is the name of the file + \item For #INDIRECT# documents this is the name of top-level + index file. All document files will be saved into the + save directory where the index file will resize. */ + virtual void save_as(const GURL &where, const bool bundled=0); + //@} + /** Returns pointer to the internal directory of the document, if it + is in one of the new formats: #BUNDLED# or #INDIRECT#. + Otherwise (if the format of the input document is obsolete), + #ZERO# is returned. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmDir> get_djvm_dir(void) const; + /** Returns pointer to the document bookmarks. + This applies to #BUNDLED# and #INDIRECT# documents. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmNav> get_djvm_nav(void) const; + /** Returns pointer to the internal directory of the document, if it + is in obsolete #OLD_BUNDLED# format. + + #ZERO# will also be returned if the initializing thread has not + learnt enough information about the document (#DOC_DIR_KNOWN# has + not been set yet). Check \Ref{is_init_complete}() and \Ref{init}() + for details. */ + GP<DjVmDir0> get_djvm_dir0(void) const; + /** Returns pointer to {\em navigation directory} of the document. + The navigation directory is a DjVu file containing only one + chunk #NDIR# inside a #FORM:DJVI# with the list of all + document pages. */ + GP<DjVuNavDir> get_nav_dir(void) const; + + /// Create a complete DjVuXML file. + void writeDjVuXML(const GP<ByteStream> &gstr_out,int flags) const; + + /// Returns TRUE if #class_name# is #"DjVuDocument"# or #"DjVuPort"# + virtual bool inherits(const GUTF8String &class_name) const; + + /// Converts the specified id to a URL. + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + virtual void notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask); + + virtual GList<GURL> get_url_names(void); + virtual void set_recover_errors(ErrorRecoveryAction=ABORT); + virtual void set_verbose_eof(bool=true); + + static void set_compress_codec( + void (*codec)(GP<ByteStream> &, const GURL &where, bool bundled)); + + static void set_import_codec( + void (*codec)(GP<DataPool> &,const GURL &url,bool &, bool &)); + +protected: + static void (*djvu_import_codec) ( + GP<DataPool> &pool, const GURL &url,bool &needs_compression, bool &needs_rename ); + static void (*djvu_compress_codec) ( + GP<ByteStream> &bs, const GURL &where, bool bundled); + virtual GP<DjVuFile> url_to_file(const GURL & url, bool dont_create=false) const; + GURL init_url; + GP<DataPool> init_data_pool; + GP<DjVmDir> djvm_dir; // New-style DjVm directory + GP<DjVmNav> djvm_nav; + int doc_type; + bool needs_compression_flag; + bool can_compress_flag; + bool needs_rename_flag; + + + + bool has_url_names; + GCriticalSection url_names_lock; + GList<GURL> url_names; + ErrorRecoveryAction recover_errors; + bool verbose_eof; +public: + class UnnamedFile; // This really should be protected ... + class ThumbReq; // This really should be protected ... +protected: + bool init_started; + GSafeFlags flags; + GSafeFlags init_thread_flags; + DjVuFileCache * cache; + GP<DjVuSimplePort> simple_port; + + GP<DjVmDir0> djvm_dir0; // Old-style DjVm directory + GP<DjVuNavDir> ndir; // Old-style navigation directory + GUTF8String first_page_name;// For OLD_BUNDLED docs only + + // The following is used in init() and destructor to query NDIR + // DO NOT USE FOR ANYTHING ELSE. THE FILE IS ZEROED IMMEDIATELY + // AFTER IT'S NO LONGER NEEDED. If you don't zero it, ~DjVuDocument() + // will kill it, which is a BAD thing if the file's already in cache. + GP<DjVuFile> ndir_file; + + GPList<UnnamedFile> ufiles_list; + GCriticalSection ufiles_lock; + + GPList<ThumbReq> threqs_list; + GCriticalSection threqs_lock; + + GP<DjVuDocument> init_life_saver; + + static const float thumb_gamma; + + // Reads document contents in another thread trying to determine + // its type and structure + GThread init_thr; + static void static_init_thread(void *); + void init_thread(void); + + void check() const; + + void process_threqs(void); + GP<ThumbReq> add_thumb_req(const GP<ThumbReq> & thumb_req); + + void add_to_cache(const GP<DjVuFile> & f); + void check_unnamed_files(void); + GUTF8String get_int_prefix(void) const; + void set_file_aliases(const DjVuFile * file); + GURL invent_url(const GUTF8String &name) const; +}; + +class DjVuDocument::UnnamedFile : public GPEnabled +{ +public: + enum { ID, PAGE_NUM }; + int id_type; + GUTF8String id; + int page_num; + GURL url; + GP<DjVuFile> file; + GP<DataPool> data_pool; +protected: + UnnamedFile(int xid_type, const GUTF8String &xid, int xpage_num, const GURL & xurl, + const GP<DjVuFile> & xfile) : + id_type(xid_type), id(xid), page_num(xpage_num), url(xurl), file(xfile) {} + friend class DjVuDocument; +}; + +class DjVuDocument::ThumbReq : public GPEnabled +{ +public: + int page_num; + GP<DataPool> data_pool; + + // Either of the next two blocks should present + GP<DjVuFile> image_file; + + int thumb_chunk; + GP<DjVuFile> thumb_file; +protected: + ThumbReq(int xpage_num, const GP<DataPool> & xdata_pool) : + page_num(xpage_num), data_pool(xdata_pool) {} + friend class DjVuDocument; +}; + +inline void +DjVuDocument::init(const GURL &url, GP<DjVuPort> port, DjVuFileCache *cache) +{ + start_init(url,port,cache); + wait_for_complete_init(); +} + +inline GP<DjVuDocument> +DjVuDocument::create( + const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache) +{ + DjVuDocument *doc=new DjVuDocument; + GP<DjVuDocument> retval=doc; + doc->start_init(url,xport,xcache); + return retval; +} + +inline bool +DjVuDocument::is_init_complete(void) const +{ + return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0; +} + +inline bool +DjVuDocument::is_init_ok(void) const +{ + return (flags & DOC_INIT_OK)!=0; +} + +inline void +DjVuDocument::set_needs_compression(void) +{ + needs_compression_flag=true; +} + +inline bool +DjVuDocument::needs_compression(void) const +{ + return needs_compression_flag; +} + +inline bool +DjVuDocument::needs_rename(void) const +{ + return needs_rename_flag; +} + +inline bool +DjVuDocument::can_compress(void) const +{ + return can_compress_flag; +} + +inline bool +DjVuDocument::is_init_failed(void) const +{ + return (flags & DOC_INIT_FAILED)!=0; +} + +inline int +DjVuDocument::get_doc_type(void) const { return doc_type; } + +inline long +DjVuDocument::get_doc_flags(void) const { return flags; } + +inline bool +DjVuDocument::is_bundled(void) const +{ + return doc_type==BUNDLED || doc_type==OLD_BUNDLED; +} + +inline GURL +DjVuDocument::get_init_url(void) const { return init_url; } + +inline GP<DataPool> +DjVuDocument::get_init_data_pool(void) const { return init_data_pool; } + +inline bool +DjVuDocument::inherits(const GUTF8String &class_name) const +{ + return + (GUTF8String("DjVuDocument") == class_name) || + DjVuPort::inherits(class_name); +// !strcmp("DjVuDocument", class_name) || +// DjVuPort::inherits(class_name); +} + +inline float +DjVuDocument::get_thumbnails_gamma(void) const +{ + return thumb_gamma; +} + +inline DjVuFileCache * +DjVuDocument::get_cache(void) const +{ + return cache; +} + +inline GP<DjVmDir> +DjVuDocument::get_djvm_dir(void) const +{ + if (doc_type==SINGLE_PAGE) + G_THROW( ERR_MSG("DjVuDocument.no_dir") ); + if (doc_type!=BUNDLED && doc_type!=INDIRECT) + G_THROW( ERR_MSG("DjVuDocument.obsolete") ); + return djvm_dir; +} + +inline GP<DjVmNav> +DjVuDocument::get_djvm_nav(void) const +{ + if (doc_type==BUNDLED || doc_type==INDIRECT) + return djvm_nav; + return 0; +} + +inline GP<DjVmDir0> +DjVuDocument::get_djvm_dir0(void) const +{ + if (doc_type!=OLD_BUNDLED) + G_THROW( ERR_MSG("DjVuDocument.old_bundle") ); + return djvm_dir0; +} + +inline GP<DjVuNavDir> +DjVuDocument::get_nav_dir(void) const +{ + return ndir; +} + +inline void +DjVuDocument::set_recover_errors(ErrorRecoveryAction recover) +{ + recover_errors=recover; +} + +inline void +DjVuDocument::set_verbose_eof(bool verbose) +{ + verbose_eof=verbose; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp new file mode 100644 index 00000000..2d977be1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp @@ -0,0 +1,353 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDumpHelper.cpp,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuDumpHelper.h" +#include "DataPool.h" +#include "DjVmDir.h" +#include "DjVuInfo.h" +#include "IFFByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#ifdef putchar +#undef putchar +#endif + +struct DjVmInfo +{ + GP<DjVmDir> dir; + GPMap<int,DjVmDir::File> map; +}; + +inline static void +putchar(ByteStream & str, char ch) +{ + str.write(&ch, 1); +} + +// ---------- ROUTINES FOR SUMMARIZING CHUNK DATA + +static void +display_djvu_info(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t size, DjVmInfo&, int) +{ + GP<DjVuInfo> ginfo=DjVuInfo::create(); + DjVuInfo &info=*ginfo; + info.decode(*iff.get_bytestream()); + if (size >= 4) + out_str.format( "DjVu %dx%d", info.width, info.height); + if (size >= 5) + out_str.format( ", v%d", info.version); + if (size >= 8) + out_str.format( ", %d dpi", info.dpi); + if (size >= 8) + out_str.format( ", gamma=%3.1f", info.gamma); +} + +static void +display_djbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 shared dictionary"); +} + +static void +display_fgbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 colors data"); +} + +static void +display_sjbz(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "JB2 bilevel data"); +} + +static void +display_smmr(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "G4/MMR stencil data"); +} + +static void +display_iw4(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + GP<ByteStream> gbs = iff.get_bytestream(); + unsigned char serial = gbs->read8(); + unsigned char slices = gbs->read8(); + out_str.format( "IW4 data #%d, %d slices", serial+1, slices); + if (serial == 0) + { + unsigned char major = gbs->read8(); + unsigned char minor = gbs->read8(); + unsigned char xhi = gbs->read8(); + unsigned char xlo = gbs->read8(); + unsigned char yhi = gbs->read8(); + unsigned char ylo = gbs->read8(); + out_str.format( ", v%d.%d (%s), %dx%d", major & 0x7f, minor, + (major & 0x80 ? "b&w" : "color"), (xhi<<8)+xlo, (yhi<<8)+ylo ); + } +} + +static void +display_djvm_dirm(ByteStream & out_str, IFFByteStream & iff, + GUTF8String head, size_t, DjVmInfo& djvminfo, int) +{ + GP<DjVmDir> dir = DjVmDir::create(); + dir->decode(iff.get_bytestream()); + GPList<DjVmDir::File> list = dir->get_files_list(); + if (dir->is_indirect()) + { + out_str.format( "Document directory (indirect, %d files %d pages)", + dir->get_files_num(), dir->get_pages_num()); + for (GPosition p=list; p; ++p) + out_str.format( "\n%s%s -> %s", (const char*)head, + (const char*)list[p]->get_load_name(), (const char*)list[p]->get_save_name() ); + } + else + { + out_str.format( "Document directory (bundled, %d files %d pages)", + dir->get_files_num(), dir->get_pages_num()); + djvminfo.dir = dir; + djvminfo.map.empty(); + for (GPosition p=list; p; ++p) + djvminfo.map[list[p]->offset] = list[p]; + } +} + +static void +display_th44(ByteStream & out_str, IFFByteStream & iff, + GUTF8String, size_t, DjVmInfo & djvminfo, int counter) +{ + int start_page=-1; + if (djvminfo.dir) + { + GPList<DjVmDir::File> files_list=djvminfo.dir->get_files_list(); + for(GPosition pos=files_list;pos;++pos) + { + GP<DjVmDir::File> frec=files_list[pos]; + if (iff.tell()>=frec->offset && + iff.tell()<frec->offset+frec->size) + { + while(pos && !files_list[pos]->is_page()) + ++pos; + if (pos) + start_page=files_list[pos]->get_page_num(); + break; + } + } + } + if (start_page>=0) + out_str.format( "Thumbnail icon for page %d", start_page+counter+1); + else + out_str.format( "Thumbnail icon"); +} + +static void +display_incl(ByteStream & out_str, IFFByteStream & iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + GUTF8String name; + char ch; + while(iff.read(&ch, 1) && ch!='\n') + name += ch; + out_str.format( "Indirection chunk --> {%s}", (const char *) name); +} + +static void +display_anno(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "Page annotation"); + GUTF8String id; + iff.short_id(id); + out_str.format( " (hyperlinks, etc.)"); +} + +static void +display_text(ByteStream & out_str, IFFByteStream &iff, + GUTF8String, size_t, DjVmInfo&, int) +{ + out_str.format( "Hidden text"); + GUTF8String id; + iff.short_id(id); + out_str.format( " (text, etc.)"); +} + +struct displaysubr +{ + const char *id; + void (*subr)(ByteStream &, IFFByteStream &, GUTF8String, + size_t, DjVmInfo&, int counter); +}; + +static displaysubr disproutines[] = +{ + { "DJVU.INFO", display_djvu_info }, + { "DJVU.Smmr", display_smmr }, + { "DJVU.Sjbz", display_sjbz }, + { "DJVU.Djbz", display_djbz }, + { "DJVU.FG44", display_iw4 }, + { "DJVU.BG44", display_iw4 }, + { "DJVU.FGbz", display_fgbz }, + { "DJVI.Sjbz", display_sjbz }, + { "DJVI.Djbz", display_djbz }, + { "DJVI.FGbz", display_fgbz }, + { "DJVI.FG44", display_iw4 }, + { "DJVI.BG44", display_iw4 }, + { "BM44.BM44", display_iw4 }, + { "PM44.PM44", display_iw4 }, + { "DJVM.DIRM", display_djvm_dirm }, + { "THUM.TH44", display_th44 }, + { "INCL", display_incl }, + { "ANTa", display_anno }, + { "ANTz", display_anno }, + { "TXTa", display_text }, + { "TXTz", display_text }, + { 0, 0 }, +}; + +// ---------- ROUTINES FOR DISPLAYING CHUNK STRUCTURE + +static void +display_chunks(ByteStream & out_str, IFFByteStream &iff, + const GUTF8String &head, DjVmInfo djvminfo) +{ + size_t size; + GUTF8String id, fullid; + GUTF8String head2 = head + " "; + GPMap<int,DjVmDir::File> djvmmap; + int rawoffset; + GMap<GUTF8String, int> counters; + + while ((size = iff.get_chunk(id, &rawoffset))) + { + if (!counters.contains(id)) counters[id]=0; + else counters[id]++; + + GUTF8String msg; + msg.format("%s%s [%d] ", (const char *)head, (const char *)id, size); + out_str.format( "%s", (const char *)msg); + // Display DJVM is when adequate + if (djvminfo.dir) + { + GP<DjVmDir::File> rec = djvminfo.map[rawoffset]; + if (rec) + out_str.format( "{%s}", (const char*) rec->get_load_name()); + } + // Test chunk type + iff.full_id(fullid); + for (int i=0; disproutines[i].id; i++) + if (fullid == disproutines[i].id || id == disproutines[i].id) + { + int n = msg.length(); + while (n++ < 14+(int) head.length()) putchar(out_str, ' '); + if (!iff.composite()) out_str.format( " "); + (*disproutines[i].subr)(out_str, iff, head2, + size, djvminfo, counters[id]); + break; + } + // Default display of composite chunk + out_str.format( "\n"); + if (iff.composite()) + display_chunks(out_str, iff, head2, djvminfo); + // Terminate + iff.close_chunk(); + } +} + +GP<ByteStream> +DjVuDumpHelper::dump(const GP<DataPool> & pool) +{ + return dump(pool->get_stream()); +} + +GP<ByteStream> +DjVuDumpHelper::dump(GP<ByteStream> gstr) +{ + GP<ByteStream> out_str=ByteStream::create(); + GUTF8String head=" "; + GP<IFFByteStream> iff=IFFByteStream::create(gstr); + DjVmInfo djvminfo; + display_chunks(*out_str, *iff, head, djvminfo); + return out_str; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h new file mode 100644 index 00000000..33be56c3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h @@ -0,0 +1,126 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuDumpHelper.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUDUMPHELPER_H +#define _DJVUDUMPHELPER_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuDupmHelper.h + This file contains code capable of generating information on + DjVu documents without actually decoding them. The code has + been extracted from a command line utility \Ref{djvudump.cpp} + for use in the DjVu plugin. + @memo + DjVu Dump Helper. + @author + L\'eon Bottou <[email protected]> -- as a separate program.\\ + Andrei Erofeev <[email protected]> -- as a class. + @version + #$Id: DjVuDumpHelper.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# */ +//@{ + + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DataPool; +class ByteStream; + +/** DjVuDumpHelper. + This class can dump information on any DjVu file without decoding it. + Based upon old \Ref{djvudump.cpp} code. + */ + +class DjVuDumpHelper +{ +public: + /// Default constructor + DjVuDumpHelper(void) {} + /// Destructor + ~DjVuDumpHelper(void) {} + /** Interprets the file passed in the \Ref{DataPool}, and returns + the results in \Ref{ByteStream}. */ + GP<ByteStream> dump(const GP<DataPool> & pool); + /** Interprets the file passed in the \Ref{ByteStream}, and returns + the results in \Ref{ByteStream}. */ + GP<ByteStream> dump(GP<ByteStream> str); +}; + + +//@} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp new file mode 100644 index 00000000..e7c74b84 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp @@ -0,0 +1,174 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuErrorList.cpp,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuErrorList.h" +#include "DjVmDoc.h" +#include "GException.h" +#include "GContainer.h" +#include "GOS.h" +#include "DataPool.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +DjVuErrorList::DjVuErrorList() {} + +GURL +DjVuErrorList::set_stream(GP<ByteStream> xibs) +{ + GUTF8String name; + static unsigned long serial=0; + pool=DataPool::create(xibs); + name.format("data://%08lx/%08lx.djvu", + ++serial,(unsigned long)(size_t)((const ByteStream *)xibs)); + pool_url=GURL::UTF8(name); + return pool_url; +} + +bool +DjVuErrorList::notify_error(const DjVuPort * source, const GUTF8String & msg) +{ + Errors.append(msg); + return 1; +} + +bool +DjVuErrorList::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + Status.append(msg); + return 1; +} + +GUTF8String +DjVuErrorList::GetError(void) +{ + GUTF8String PrevError; + GPosition pos; + if((pos=Errors)) + { + PrevError=Errors[pos]; + Errors.del(pos); + } + return PrevError; +} + +GUTF8String +DjVuErrorList::GetStatus(void) +{ + GUTF8String PrevStatus; + GPosition pos; + if((pos=Status)) + { + PrevStatus=Status[pos]; + Status.del(pos); + } + return PrevStatus; +} + +GP<DataPool> +DjVuErrorList::request_data(const DjVuPort * source, const GURL & url) +{ + GP<DataPool> retval; + G_TRY + { + if (pool && url.protocol().downcase() == "data") + { + if(url == pool_url) + { + retval=pool; + }else if(url.base() == pool_url) + { + GUTF8String name=url.fname(); + GP<DjVmDoc> doc=DjVmDoc::create(); + GP<ByteStream> bs=pool->get_stream(); + doc->read(*bs); + retval=doc->get_data(name); + } + }else if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + retval=DataPool::create(url); + } + } + G_CATCH_ALL + { + retval=0; + } G_ENDCATCH; + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h new file mode 100644 index 00000000..885e76aa --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h @@ -0,0 +1,194 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuErrorList.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUERRORLIST_H +#define _DJVUERRORLIST_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVuErrorList.h + This file implements a very simple class for redirecting port caster + messages that would normally end up on stderr to a double linked list. + + @memo DjVuErrorList class. + @author Bill C Riemers <[email protected]> + @version #$Id: DjVuErrorList.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuErrorList# provides a convenient way to redirect error messages + from classes derived from DjVuPort to a list that can be accessed at + any time. */ + +class DjVuErrorList : public DjVuSimplePort +{ +protected: + /// The normal port caster constructor. + DjVuErrorList(void); +public: + static GP<DjVuErrorList> create(void) {return new DjVuErrorList();} + + /// This constructor allows the user to specify the ByteStream. + GURL set_stream(GP<ByteStream>); + + /// Append all error messages to the list + virtual bool notify_error(const DjVuPort * source, const GUTF8String & msg); + + /// Append all status messages to the list + virtual bool notify_status(const DjVuPort * source, const GUTF8String & msg); + + /// Add a new class to have its messages redirected here. + inline void connect( const DjVuPort &port); + + /// Get the listing of errors, and clear the list. + inline GList<GUTF8String> GetErrorList(void); + + /// Just clear the list. + inline void ClearError(void); + + /// Get one error message and clear that message from the list. + GUTF8String GetError(void); + + /// Check if there are anymore error messages. + inline bool HasError(void) const; + + /// Get the listing of status messages, and clear the list. + inline GList<GUTF8String> GetStatusList(void); + + /// Just clear the list. + inline void ClearStatus(void); + + /// Get one status message and clear that message from the list. + GUTF8String GetStatus(void); + + /// Check if there are any more status messages. + inline bool HasStatus(void) const; + + /** This gets the data. We can't use the simple port's request + data since we want to allow the user to specify the ByteStream. */ + virtual GP<DataPool> request_data ( + const DjVuPort * source, const GURL & url ); + +private: + GURL pool_url; + GP<DataPool> pool; + GList<GUTF8String> Errors; + GList<GUTF8String> Status; +private: //dummy stuff + static GURL set_stream(ByteStream *); +}; + +inline void +DjVuErrorList::connect( const DjVuPort &port ) +{ get_portcaster()->add_route(&port, this); } + +inline GList<GUTF8String> +DjVuErrorList::GetErrorList(void) +{ + GList<GUTF8String> retval=(const GList<GUTF8String>)Errors; + Errors.empty(); + return retval; +} + +inline void +DjVuErrorList::ClearError(void) +{ Errors.empty(); } + +inline GList<GUTF8String> +DjVuErrorList::GetStatusList(void) +{ + GList<GUTF8String> retval=(const GList<GUTF8String>)Status; + Status.empty(); + return retval; +} + +inline void +DjVuErrorList::ClearStatus(void) +{ Status.empty(); } + +inline bool +DjVuErrorList::HasError(void) const +{ return !Errors.isempty(); } + +inline bool +DjVuErrorList::HasStatus(void) const +{ return !Status.isempty(); } + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp new file mode 100644 index 00000000..73e3a9c2 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp @@ -0,0 +1,2831 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFile.cpp,v 1.11 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuFile.h" +#include "IFFByteStream.h" +#include "GOS.h" +#include "MMRDecoder.h" +#ifdef NEED_JPEG_DECODER +#include "JPEGDecoder.h" +#endif +#include "DjVuAnno.h" +#include "DjVuText.h" +#include "DataPool.h" +#include "JB2Image.h" +#include "IW44Image.h" +#include "DjVuNavDir.h" +#ifndef NEED_DECODER_ONLY +#include "BSByteStream.h" +#endif // NEED_DECODER_ONLY + +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + + +#define REPORT_EOF(x) \ + {G_TRY{G_THROW( ByteStream::EndOfFile );}G_CATCH(ex){report_error(ex,(x));}G_ENDCATCH;} + +static GP<GPixmap> (*djvu_decode_codec)(ByteStream &bs)=0; + +class ProgressByteStream : public ByteStream +{ +public: + ProgressByteStream(const GP<ByteStream> & xstr) : str(xstr), + last_call_pos(0) {} + virtual ~ProgressByteStream() {} + + virtual size_t read(void *buffer, size_t size) + { + int rc=0; + // G_TRY {} CATCH; block here is merely to avoid egcs internal error + G_TRY { + int cur_pos=str->tell(); + if (progress_cb && (last_call_pos/256!=cur_pos/256)) + { + progress_cb(cur_pos, progress_cl_data); + last_call_pos=cur_pos; + } + rc=str->read(buffer, size); + } G_CATCH_ALL { + G_RETHROW; + } G_ENDCATCH; + return rc; + } + virtual size_t write(const void *buffer, size_t size) + { + return str->write(buffer, size); + } + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false) + { + return str->seek(offset, whence); + } + virtual long tell(void ) const { return str->tell(); } + + void set_progress_cb(void (* xprogress_cb)(int, void *), + void * xprogress_cl_data) + { + progress_cb=xprogress_cb; + progress_cl_data=xprogress_cl_data; + } +private: + GP<ByteStream> str; + void * progress_cl_data; + void (* progress_cb)(int pos, void *); + int last_call_pos; + + // Cancel C++ default stuff + ProgressByteStream & operator=(const ProgressByteStream &); +}; + + +DjVuFile::DjVuFile() +: file_size(0), recover_errors(ABORT), verbose_eof(false), chunks_number(-1), +initialized(false) +{ +} + +void +DjVuFile::check() const +{ + if (!initialized) + G_THROW( ERR_MSG("DjVuFile.not_init") ); +} + +GP<DjVuFile> +DjVuFile::create( + const GP<ByteStream> & str, const ErrorRecoveryAction recover_errors, + const bool verbose_eof ) +{ + DjVuFile *file=new DjVuFile(); + GP<DjVuFile> retval=file; + file->set_recover_errors(recover_errors); + file->set_verbose_eof(verbose_eof); + file->init(str); + return retval; +} + +void +DjVuFile::init(const GP<ByteStream> & str) +{ + DEBUG_MSG("DjVuFile::DjVuFile(): ByteStream constructor\n"); + DEBUG_MAKE_INDENT(3); + + if (initialized) + G_THROW( ERR_MSG("DjVuFile.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuFile.not_secured") ); + + file_size=0; + decode_thread=0; + + // Read the data from the stream + data_pool=DataPool::create(str); + + // Construct some dummy URL + GUTF8String buffer; + buffer.format("djvufile:/%p.djvu", this); + DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)buffer<<"\n"); + url=GURL::UTF8(buffer); + + // Set it here because trigger will call other DjVuFile's functions + initialized=true; + + // Add (basically - call) the trigger + data_pool->add_trigger(-1, static_trigger_cb, this); +} + +GP<DjVuFile> +DjVuFile::create( + const GURL & xurl, GP<DjVuPort> port, + const ErrorRecoveryAction recover_errors, const bool verbose_eof ) +{ + DjVuFile *file=new DjVuFile(); + GP<DjVuFile> retval=file; + file->set_recover_errors(recover_errors); + file->set_verbose_eof(verbose_eof); + file->init(xurl,port); + return retval; +} + +void +DjVuFile::init(const GURL & xurl, GP<DjVuPort> port) +{ + DEBUG_MSG("DjVuFile::init(): url='" << xurl << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (initialized) + G_THROW( ERR_MSG("DjVuFile.2nd_init") ); + if (!get_count()) + G_THROW( ERR_MSG("DjVuFile.not_secured") ); + if (xurl.is_empty()) + G_THROW( ERR_MSG("DjVuFile.empty_URL") ); + + url = xurl; + DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)url<<"\n"); + file_size=0; + decode_thread=0; + + DjVuPortcaster * pcaster=get_portcaster(); + + // We need it 'cause we're waiting for our own termination in stop_decode() + pcaster->add_route(this, this); + if (!port) + port = simple_port = new DjVuSimplePort(); + pcaster->add_route(this, port); + + // Set it here because trigger will call other DjVuFile's functions + initialized=true; + + if (!(data_pool=DataPool::create(pcaster->request_data(this, url)))) + G_THROW( ERR_MSG("DjVuFile.no_data") "\t"+url.get_string()); + data_pool->add_trigger(-1, static_trigger_cb, this); +} + +DjVuFile::~DjVuFile(void) +{ + DEBUG_MSG("DjVuFile::~DjVuFile(): destroying...\n"); + DEBUG_MAKE_INDENT(3); + + // No more messages. They may result in adding this file to a cache + // which will be very-very bad as we're being destroyed + get_portcaster()->del_port(this); + + // Unregister the trigger (we don't want it to be called and attempt + // to access the destroyed object) + if (data_pool) + data_pool->del_trigger(static_trigger_cb, this); + + // We don't have to wait for decoding to finish here. It's already + // finished (we know it because there is a "life saver" in the + // thread function) -- but we need to delete it + delete decode_thread; decode_thread=0; +} + +void +DjVuFile::reset(void) +{ + flags.enter(); + info = 0; + anno = 0; + text = 0; + meta = 0; + bg44 = 0; + fgbc = 0; + fgjb = 0; + fgjd = 0; + fgpm = 0; + dir = 0; + description = ""; + mimetype = ""; + flags=(flags&(ALL_DATA_PRESENT|DECODE_STOPPED|DECODE_FAILED)); + flags.leave(); +} + +unsigned int +DjVuFile::get_memory_usage(void) const +{ + unsigned int size=sizeof(*this); + if (info) size+=info->get_memory_usage(); + if (bg44) size+=bg44->get_memory_usage(); + if (fgjb) size+=fgjb->get_memory_usage(); + if (fgpm) size+=fgpm->get_memory_usage(); + if (fgbc) size+=fgbc->size()*sizeof(int); + if (anno) size+=anno->size(); + if (meta) size+=meta->size(); + if (dir) size+=dir->get_memory_usage(); + return size; +} + +GPList<DjVuFile> +DjVuFile::get_included_files(bool only_created) +{ + check(); + if (!only_created && !are_incl_files_created()) + process_incl_chunks(); + + GCriticalSectionLock lock(&inc_files_lock); + GPList<DjVuFile> list=inc_files_list; // Get a copy when locked + return list; +} + +void +DjVuFile::wait_for_chunk(void) +// Will return after a chunk has been decoded +{ + check(); + DEBUG_MSG("DjVuFile::wait_for_chunk() called\n"); + DEBUG_MAKE_INDENT(3); + chunk_mon.enter(); + chunk_mon.wait(); + chunk_mon.leave(); +} + +bool +DjVuFile::wait_for_finish(bool self) +// if self==TRUE, will block until decoding of this file is over +// if self==FALSE, will block until decoding of a child (direct +// or indirect) is over. +// Will return FALSE if there is nothing to wait for. TRUE otherwise +{ + DEBUG_MSG("DjVuFile::wait_for_finish(): self=" << self <<"\n"); + DEBUG_MAKE_INDENT(3); + + check(); + + if (self) + { + // It's best to check for self termination using flags. The reason + // is that finish_mon is updated in a DjVuPort function, which + // will not be called if the object is being destroyed + GMonitorLock lock(&flags); + if (is_decoding()) + { + while(is_decoding()) flags.wait(); + DEBUG_MSG("got it\n"); + return 1; + } + } else + { + // By locking the monitor, we guarantee that situation doesn't change + // between the moments when we check for pending finish events + // and when we actually run wait(). If we don't lock, the last child + // may terminate in between, and we'll wait forever. + // + // Locking is required by GMonitor interface too, btw. + GMonitorLock lock(&finish_mon); + GP<DjVuFile> file; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decoding()) + { + file=f; break; + } + } + } + if (file) + { + finish_mon.wait(); + DEBUG_MSG("got it\n"); + return 1; + } + } + DEBUG_MSG("nothing to wait for\n"); + return 0; +} + +void +DjVuFile::notify_chunk_done(const DjVuPort *, const GUTF8String &) +{ + check(); + chunk_mon.enter(); + chunk_mon.broadcast(); + chunk_mon.leave(); +} + +void +DjVuFile::notify_file_flags_changed(const DjVuFile * src, + long set_mask, long clr_mask) +{ + check(); + if (set_mask & (DECODE_OK | DECODE_FAILED | DECODE_STOPPED)) + { + // Signal threads waiting for file termination + finish_mon.enter(); + finish_mon.broadcast(); + finish_mon.leave(); + + // In case a thread is still waiting for a chunk + chunk_mon.enter(); + chunk_mon.broadcast(); + chunk_mon.leave(); + } + + if ((set_mask & ALL_DATA_PRESENT) && src!=this && + are_incl_files_created() && is_data_present()) + { + if (src!=this && are_incl_files_created() && is_data_present()) + { + // Check if all children have data + bool all=true; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + if (!inc_files_list[pos]->is_all_data_present()) + { + all=false; + break; + } + } + if (all) + { + DEBUG_MSG("Just got ALL data for '" << url << "'\n"); + flags|=ALL_DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0); + } + } + } +} + +void +DjVuFile::static_decode_func(void * cl_data) +{ + DjVuFile * th=(DjVuFile *) cl_data; + + /* Please do not undo this life saver. If you do then try to resolve the + following conflict first: + 1. Decoding starts and there is only one external reference + to the DjVuFile. + 2. Decoding proceeds and calls DjVuPortcaster::notify_error(), + which creates inside a temporary GP<DjVuFile>. + 3. While notify_error() is running, the only external reference + is lost, but the DjVuFile is still alive (remember the + temporary GP<>?) + 4. The notify_error() returns, the temporary GP<> gets destroyed + and the DjVuFile is attempting to destroy right in the middle + of the decoding thread. This is either a dead block (waiting + for the termination of the decoding from the ~DjVuFile() called + from the decoding thread) or coredump. */ + GP<DjVuFile> life_saver=th; + th->decode_life_saver=0; + G_TRY { + th->decode_func(); + } G_CATCH_ALL { + } G_ENDCATCH; +} + +void +DjVuFile::decode_func(void) +{ + check(); + DEBUG_MSG("DjVuFile::decode_func() called, url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + G_TRY { + const GP<ByteStream> decode_stream(decode_data_pool->get_stream()); + ProgressByteStream *pstr=new ProgressByteStream(decode_stream); + const GP<ByteStream> gpstr(pstr); + pstr->set_progress_cb(progress_cb, this); + + decode(gpstr); + + // Wait for all child files to finish + while(wait_for_finish(0)) + continue; + + DEBUG_MSG("waiting for children termination\n"); + // Check for termination status + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decode_failed()) + G_THROW( ERR_MSG("DjVuFile.decode_fail") ); + if (f->is_decode_stopped()) + G_THROW( DataPool::Stop ); + if (!f->is_decode_ok()) + { + DEBUG_MSG("this_url='" << url << "'\n"); + DEBUG_MSG("incl_url='" << f->get_url() << "'\n"); + DEBUG_MSG("decoding=" << f->is_decoding() << "\n"); + DEBUG_MSG("status='" << f->get_flags() << "\n"); + G_THROW( ERR_MSG("DjVuFile.not_finished") ); + } + } + } G_CATCH(exc) { + G_TRY { + if (!exc.cmp_cause(DataPool::Stop)) + { + flags.enter(); + flags=flags & ~DECODING | DECODE_STOPPED; + flags.leave(); + pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.stopped")) + + GUTF8String("\t") + GUTF8String(url)); + pcaster->notify_file_flags_changed(this, DECODE_STOPPED, DECODING); + } else + { + flags.enter(); + flags=flags & ~DECODING | DECODE_FAILED; + flags.leave(); + pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.failed")) + + GUTF8String("\t") + GUTF8String(url)); + pcaster->notify_error(this, exc.get_cause()); + pcaster->notify_file_flags_changed(this, DECODE_FAILED, DECODING); + } + } G_CATCH_ALL + { + DEBUG_MSG("******* Oops. Almost missed an exception\n"); + } G_ENDCATCH; + } G_ENDCATCH; + + decode_data_pool->clear_stream(); + G_TRY { + if (flags.test_and_modify(DECODING, 0, DECODE_OK | INCL_FILES_CREATED, DECODING)) + pcaster->notify_file_flags_changed(this, DECODE_OK | INCL_FILES_CREATED, + DECODING); + } G_CATCH_ALL {} G_ENDCATCH; + DEBUG_MSG("decoding thread for url='" << url << "' ended\n"); +} + +GP<DjVuFile> +DjVuFile::process_incl_chunk(ByteStream & str, int file_num) +{ + check(); + DEBUG_MSG("DjVuFile::process_incl_chunk(): processing INCL chunk...\n"); + DEBUG_MAKE_INDENT(3); + + DjVuPortcaster * pcaster=get_portcaster(); + + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=str.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + + if (incl_str.length()>0) + { + if (strchr(incl_str, '/')) + G_THROW( ERR_MSG("DjVuFile.malformed") ); + + DEBUG_MSG("incl_str='" << incl_str << "'\n"); + + GURL incl_url=pcaster->id_to_url(this, incl_str); + if (incl_url.is_empty()) // Fallback. Should never be used. + incl_url=GURL::UTF8(incl_str,url.base()); + + // Now see if there is already a file with this *name* created + { + GCriticalSectionLock lock(&inc_files_lock); + GPosition pos; + for(pos=inc_files_list;pos;++pos) + { + if (inc_files_list[pos]->url.fname()==incl_url.fname()) + break; + } + if (pos) + return inc_files_list[pos]; + } + + // No. We have to request a new file + GP<DjVuFile> file; + G_TRY + { + file=pcaster->id_to_file(this, incl_str); + } + G_CATCH(ex) + { + unlink_file(incl_str); + // In order to keep compatibility with the previous + // release of the DjVu plugin, we will not interrupt + // decoding here. We will just report the error. + // NOTE, that it's now the responsibility of the + // decoder to resolve all chunk dependencies, and + // abort decoding if necessary. + + // G_EXTHROW(ex); /* commented out */ + + get_portcaster()->notify_error(this,ex.get_cause()); + return 0; + } + G_ENDCATCH; + if (!file) + { + G_THROW( ERR_MSG("DjVuFile.no_create") "\t"+incl_str); + } + if (recover_errors!=ABORT) + file->set_recover_errors(recover_errors); + if (verbose_eof) + file->set_verbose_eof(verbose_eof); + pcaster->add_route(file, this); + + // We may have been stopped. Make sure the child will be stopped too. + if (flags & STOPPED) + file->stop(false); + if (flags & BLOCKED_STOPPED) + file->stop(true); + + // Lock the list again and check if the file has already been + // added by someone else + { + GCriticalSectionLock lock(&inc_files_lock); + GPosition pos; + for(pos=inc_files_list;pos;++pos) + { + if (inc_files_list[pos]->url.fname()==incl_url.fname()) + break; + } + if (pos) + { + file=inc_files_list[pos]; + } else if (file_num<0 || !(pos=inc_files_list.nth(file_num))) + { + inc_files_list.append(file); + } else + { + inc_files_list.insert_before(pos, file); + } + } + return file; + } + return 0; +} + + +void +DjVuFile::report_error(const GException &ex,bool throw_errors) +{ + data_pool->clear_stream(); + if((!verbose_eof)|| (ex.cmp_cause(ByteStream::EndOfFile))) + { + if(throw_errors) + { + G_EXTHROW(ex); + }else + { + get_portcaster()->notify_error(this,ex.get_cause()); + } + }else + { + GURL url=get_url(); + GUTF8String url_str=url.get_string(); +// if (url.is_local_file_url()) +// url_str=url.filename(); + + GUTF8String msg = GUTF8String( ERR_MSG("DjVuFile.EOF") "\t") + url; + if(throw_errors) + { + G_EXTHROW(ex, msg); + }else + { + get_portcaster()->notify_error(this,msg); + } + } +} + +void +DjVuFile::process_incl_chunks(void) +// This function may block for data +// NOTE: It may be called again when INCL_FILES_CREATED is set. +// It happens in insert_file() when it has modified the data +// and wants to create the actual file +{ + DEBUG_MSG("DjVuFile::process_incl_chunks(void)\n"); + DEBUG_MAKE_INDENT(3); + check(); + + int incl_cnt=0; + + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (iff.get_chunk(chkid)) + { + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid=="INCL") + { + G_TRY + { + process_incl_chunk(*iff.get_bytestream(), incl_cnt++); + } + G_CATCH(ex); + { + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + }else if(chkid=="FAKE") + { + set_needs_compression(true); + set_can_compress(true); + }else if(chkid=="BGjp") + { + set_can_compress(true); + }else if(chkid=="Smmr") + { + set_can_compress(true); + } + iff.seek_close_chunk(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + } + flags|=INCL_FILES_CREATED; + data_pool->clear_stream(); +} + +GP<JB2Dict> +DjVuFile::static_get_fgjd(void *arg) +{ + DjVuFile *file = (DjVuFile*)arg; + return file->get_fgjd(1); +} + +GP<JB2Dict> +DjVuFile::get_fgjd(int block) +{ + check(); + + // Simplest case + if (fgjd) + return fgjd; + // Check wether included files + chunk_mon.enter(); + G_TRY { + for(;;) + { + int active = 0; + GPList<DjVuFile> incs = get_included_files(); + for (GPosition pos=incs.firstpos(); pos; ++pos) + { + GP<DjVuFile> file = incs[pos]; + if (file->is_decoding()) + active = 1; + GP<JB2Dict> fgjd = file->get_fgjd(); + if (fgjd) + { + chunk_mon.leave(); + return fgjd; + } + } + // Exit if non-blocking mode + if (! block) + break; + // Exit if there is no decoding activity + if (! active) + break; + // Wait until a new chunk gets decoded + wait_for_chunk(); + } + } G_CATCH_ALL { + chunk_mon.leave(); + G_RETHROW; + } G_ENDCATCH; + chunk_mon.leave(); + if (is_decode_stopped()) G_THROW( DataPool::Stop ); + return 0; +} + +int +DjVuFile::get_dpi(int w, int h) +{ + int dpi=0, red=1; + if (info) + { + for(red=1; red<=12; red++) + if ((info->width+red-1)/red==w) + if ((info->height+red-1)/red==h) + break; + if (red>12) + G_THROW( ERR_MSG("DjVuFile.corrupt_BG44") ); + dpi=info->dpi; + } + return (dpi ? dpi : 300)/red; +} + +static inline bool +is_info(const GUTF8String &chkid) +{ + return (chkid=="INFO"); +} + +static inline bool +is_annotation(const GUTF8String &chkid) +{ + return (chkid=="ANTa" || + chkid=="ANTz" || + chkid=="FORM:ANNO" ); +} + +static inline bool +is_text(const GUTF8String &chkid) +{ + return (chkid=="TXTa" || chkid=="TXTz"); +} + +static inline bool +is_meta(const GUTF8String &chkid) +{ + return (chkid=="METa" || chkid=="METz"); +} + + +GUTF8String +DjVuFile::decode_chunk( const GUTF8String &id, const GP<ByteStream> &gbs, + bool djvi, bool djvu, bool iw44) +{ + DEBUG_MSG("DjVuFile::decode_chunk()\n"); + ByteStream &bs=*gbs; + check(); + + // If this object is referenced by only one GP<> pointer, this + // pointer should be the "life_saver" created by the decoding thread. + // If it is the only GP<> pointer, then nobody is interested in the + // results of the decoding and we can abort now with #DataPool::Stop# + if (get_count()==1) + G_THROW( DataPool::Stop ); + + GUTF8String desc = ERR_MSG("DjVuFile.unrecog_chunk"); + GUTF8String chkid = id; + DEBUG_MSG("DjVuFile::decode_chunk() : decoding " << id << "\n"); + + // INFO (information chunk for djvu page) + if (is_info(chkid) && (djvu || djvi)) + { + if (info) + G_THROW( ERR_MSG("DjVuFile.corrupt_dupl") ); + if (djvi) + G_THROW( ERR_MSG("DjVuFile.corrupt_INFO") ); + // DjVuInfo::decode no longer throws version exceptions + GP<DjVuInfo> xinfo=DjVuInfo::create(); + xinfo->decode(bs); + info = xinfo; + desc.format( ERR_MSG("DjVuFile.page_info") ); + // Consistency checks (previously in DjVuInfo::decode) + if (info->width<0 || info->height<0) + G_THROW( ERR_MSG("DjVuFile.corrupt_zero") ); + if (info->version >= DJVUVERSION_TOO_NEW) + G_THROW( ERR_MSG("DjVuFile.new_version") "\t" STRINGIFY(DJVUVERSION_TOO_NEW) ); + if(info->compressable) + set_can_compress(true); + } + + // INCL (inclusion chunk) + else if (chkid == "INCL" && (djvi || djvu || iw44)) + { + GP<DjVuFile> file=process_incl_chunk(bs); + if (file) + { + int decode_was_already_started = 1; + { + GMonitorLock lock(&file->flags); + // Start decoding + if(file->resume_decode()) + { + decode_was_already_started = 0; + } + } + // Send file notifications if previously started + if (decode_was_already_started) + { + // May send duplicate notifications... + if (file->is_decode_ok()) + get_portcaster()->notify_file_flags_changed(file, DECODE_OK, 0); + else if (file->is_decode_failed()) + get_portcaster()->notify_file_flags_changed(file, DECODE_FAILED, 0); + } + desc.format( ERR_MSG("DjVuFile.indir_chunk1") "\t" + file->get_url().fname() ); + } else + desc.format( ERR_MSG("DjVuFile.indir_chunk2") ); + } + + // Djbz (JB2 Dictionary) + else if (chkid == "Djbz" && (djvu || djvi)) + { + if (this->fgjd) + G_THROW( ERR_MSG("DjVuFile.dupl_Dxxx") ); + if (this->fgjd) + G_THROW( ERR_MSG("DjVuFile.Dxxx_after_Sxxx") ); + GP<JB2Dict> fgjd = JB2Dict::create(); + fgjd->decode(gbs); + this->fgjd = fgjd; + desc.format( ERR_MSG("DjVuFile.shape_dict") "\t%d", fgjd->get_shape_count() ); + } + + // Sjbz (JB2 encoded mask) + else if (chkid=="Sjbz" && (djvu || djvi)) + { + if (this->fgjb) + G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") ); + GP<JB2Image> fgjb=JB2Image::create(); + // ---- begin hack + if (info && info->version <=18) + fgjb->reproduce_old_bug = true; + // ---- end hack + fgjb->decode(gbs, static_get_fgjd, (void*)this); + this->fgjb = fgjb; + desc.format( ERR_MSG("DjVuFile.fg_mask") "\t%d\t%d\t%d", + fgjb->get_width(), fgjb->get_height(), + get_dpi(fgjb->get_width(), fgjb->get_height())); + } + + // Smmr (MMR-G4 encoded mask) + else if (chkid=="Smmr" && (djvu || djvi)) + { + if (this->fgjb) + G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") ); + set_can_compress(true); + this->fgjb = MMRDecoder::decode(gbs); + desc.format( ERR_MSG("DjVuFile.G4_mask") "\t%d\t%d\t%d", + fgjb->get_width(), fgjb->get_height(), + get_dpi(fgjb->get_width(), fgjb->get_height())); + } + + // BG44 (background wavelets) + else if (chkid == "BG44" && (djvu || djvi)) + { + if (!bg44) + { + if (bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + // First chunk + GP<IW44Image> bg44=IW44Image::create_decode(IW44Image::COLOR); + bg44->decode_chunk(gbs); + this->bg44 = bg44; + desc.format( ERR_MSG("DjVuFile.IW44_bg1") "\t%d\t%d\t%d", + bg44->get_width(), bg44->get_height(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + else + { + // Refinement chunks + GP<IW44Image> bg44 = this->bg44; + bg44->decode_chunk(gbs); + desc.format( ERR_MSG("DjVuFile.IW44_bg2") "\t%d\t%d", + bg44->get_serial(), get_dpi(bg44->get_width(), bg44->get_height())); + } + } + + // FG44 (foreground wavelets) + else if (chkid == "FG44" && (djvu || djvu)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + GP<IW44Image> gfg44=IW44Image::create_decode(IW44Image::COLOR); + IW44Image &fg44=*gfg44; + fg44.decode_chunk(gbs); + fgpm=fg44.get_pixmap(); + desc.format( ERR_MSG("DjVuFile.IW44_fg") "\t%d\t%d\t%d", + fg44.get_width(), fg44.get_height(), + get_dpi(fg44.get_width(), fg44.get_height())); + } + + // LINK (background LINK) + else if (chkid == "LINK" && (djvu || djvi)) + { + if (bg44 || bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + if(djvu_decode_codec) + { + set_modified(true); + set_can_compress(true); + set_needs_compression(true); + this->bgpm = djvu_decode_codec(bs); + desc.format( ERR_MSG("DjVuFile.color_import1") "\t%d\t%d\t%d", + bgpm->columns(), bgpm->rows(), + get_dpi(bgpm->columns(), bgpm->rows())); + }else + { + desc.format( ERR_MSG("DjVuFile.color_import2") ); + } + } + + // BGjp (background JPEG) + else if (chkid == "BGjp" && (djvu || djvi)) + { + if (bg44 || bgpm) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + set_can_compress(true); +#ifdef NEED_JPEG_DECODER + this->bgpm = JPEGDecoder::decode(bs); + desc.format( ERR_MSG("DjVuFile.JPEG_bg1") "\t%d\t%d\t%d", + bgpm->columns(), bgpm->rows(), + get_dpi(bgpm->columns(), bgpm->rows())); +#else + desc.format( ERR_MSG("DjVuFile.JPEG_bg2") ); +#endif + } + + // FGjp (foreground JPEG) + else if (chkid == "FGjp" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); +#ifdef NEED_JPEG_DECODER + this->fgpm = JPEGDecoder::decode(bs); + desc.format( ERR_MSG("DjVuFile.JPEG_fg1") "\t%d\t%d\t%d", + fgpm->columns(), fgpm->rows(), + get_dpi(fgpm->columns(), fgpm->rows())); +#else + desc.format( ERR_MSG("DjVuFile.JPEG_fg2") ); +#endif + } + + // BG2k (background JPEG-2000) Note: JPEG2K bitstream not finalized. + else if (chkid == "BG2k" && (djvu || djvi)) + { + if (bg44) + G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") ); + desc.format( ERR_MSG("DjVuFile.JPEG2K_bg") ); + } + + // FG2k (foreground JPEG-2000) Note: JPEG2K bitstream not finalized. + else if (chkid == "FG2k" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + desc.format( ERR_MSG("DjVuFile.JPEG2K_fg") ); + } + + // FGbz (foreground color vector) + else if (chkid == "FGbz" && (djvu || djvi)) + { + if (fgpm || fgbc) + G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") ); + GP<DjVuPalette> fgbc = DjVuPalette::create(); + fgbc->decode(gbs); + this->fgbc = fgbc; + desc.format( ERR_MSG("DjVuFile.JB2_fg") "\t%d\t%d", + fgbc->size(), fgbc->colordata.size()); + } + + // BM44/PM44 (IW44 data) + else if ((chkid == "PM44" || chkid=="BM44") && iw44) + { + if (!bg44) + { + // First chunk + GP<IW44Image> bg44 = IW44Image::create_decode(IW44Image::COLOR); + bg44->decode_chunk(gbs); + GP<DjVuInfo> info = DjVuInfo::create(); + info->width = bg44->get_width(); + info->height = bg44->get_height(); + info->dpi = 100; + this->bg44 = bg44; + this->info = info; + desc.format( ERR_MSG("DjVuFile.IW44_data1") "\t%d\t%d\t%d", + bg44->get_width(), bg44->get_height(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + else + { + // Refinement chunks + GP<IW44Image> bg44 = this->bg44; + bg44->decode_chunk(gbs); + desc.format( ERR_MSG("DjVuFile.IW44_data2") "\t%d\t%d", + bg44->get_serial(), + get_dpi(bg44->get_width(), bg44->get_height())); + } + } + + // NDIR (obsolete navigation chunk) + else if (chkid == "NDIR") + { + GP<DjVuNavDir> dir=DjVuNavDir::create(url); + dir->decode(bs); + this->dir=dir; + desc.format( ERR_MSG("DjVuFile.nav_dir") ); + } + + // FORM:ANNO (obsolete) (must be before other annotations) + else if (chkid == "FORM:ANNO") + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&anno_lock); + if (! anno) + { + anno=ByteStream::create(); + } + anno->seek(0,SEEK_END); + if (anno->tell()) + { + anno->write((void*)"", 1); + } + // Copy data + anno->copy(achunk); + desc.format( ERR_MSG("DjVuFile.anno1") ); + } + + // ANTa/ANTx/TXTa/TXTz annotations + else if (is_annotation(chkid)) // but not FORM:ANNO + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&anno_lock); + if (! anno) + { + anno = ByteStream::create(); + } + anno->seek(0,SEEK_END); + if (anno->tell() & 1) + { + anno->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(anno)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); + desc.format( ERR_MSG("DjVuFile.anno2") ); + } + else if (is_text(chkid)) + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&text_lock); + if (! text) + { + text = ByteStream::create(); + } + text->seek(0,SEEK_END); + if (text->tell()) + { + text->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(text)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); + desc.format( ERR_MSG("DjVuFile.text") ); + } + else if (is_meta(chkid)) + { + const GP<ByteStream> gachunk(ByteStream::create()); + ByteStream &achunk=*gachunk; + achunk.copy(bs); + achunk.seek(0); + GCriticalSectionLock lock(&text_lock); + if (! meta) + { + meta = ByteStream::create(); + } + meta->seek(0,SEEK_END); + if (meta->tell()) + { + meta->write((const void*)"", 1); + } + // Recreate chunk header + const GP<IFFByteStream> giffout(IFFByteStream::create(meta)); + IFFByteStream &iffout=*giffout; + iffout.put_chunk(id); + iffout.copy(achunk); + iffout.close_chunk(); +// desc.format( ERR_MSG("DjVuFile.text") ); + } + + // Return description + return desc; +} + +void +DjVuFile::set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs)) +{ + djvu_decode_codec=codec; +} + +void +DjVuFile::decode(const GP<ByteStream> &gbs) +{ + check(); + DEBUG_MSG("DjVuFile::decode(), url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + DjVuPortcaster * pcaster=get_portcaster(); + + // Get form chunk + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(gbs)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + // Check file format + bool djvi = (chkid=="FORM:DJVI")?true:false; + bool djvu = (chkid=="FORM:DJVU")?true:false; + bool iw44 = ((chkid=="FORM:PM44") || (chkid=="FORM:BM44")); + if (djvi || djvu) + mimetype = "image/x.djvu"; + else if (iw44) + mimetype = "image/x-iw44"; + else + G_THROW( ERR_MSG("DjVuFile.unexp_image") ); + + // Process chunks + int size_so_far=iff.tell(); + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + + // Decode and get chunk description + GUTF8String str = decode_chunk(chkid, iff.get_bytestream(), djvi, djvu, iw44); + // Add parameters to the chunk description to give the size and chunk id + GUTF8String desc; + desc.format("\t%5.1f\t%s", chksize/1024.0, (const char*)chkid); + // Append the whole thing to the growing file description + description = description + str + desc + "\n"; + + pcaster->notify_chunk_done(this, chkid); + // Close chunk + iff.seek_close_chunk(); + // Record file size + size_so_far=iff.tell(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + // Record file size + file_size=size_so_far; + // Close form chunk + iff.close_chunk(); + // Close BG44 codec + if (bg44) + bg44->close_codec(); + + // Complete description + if (djvu && !info) + G_THROW( ERR_MSG("DjVuFile.corrupt_missing_info") ); + if (iw44 && !info) + G_THROW( ERR_MSG("DjVuFile.corrupt_missing_IW44") ); + if (info) + { + GUTF8String desc; + if (djvu || djvi) + desc.format( ERR_MSG("DjVuFile.djvu_header") "\t%d\t%d\t%d\t%d", + info->width, info->height, + info->dpi, info->version); + else if (iw44) + desc.format( ERR_MSG("DjVuFile.IW44_header") "\t%d\t%d\t%d", + info->width, info->height, info->dpi); + description=desc + "\n" + description; + int rawsize=info->width*info->height*3; + desc.format( ERR_MSG("DjVuFile.ratio") "\t%0.1f\t%0.1f", + (double)rawsize/file_size, file_size/1024.0 ); + description=description+desc; + } +} + +void +DjVuFile::start_decode(void) +{ + check(); + DEBUG_MSG("DjVuFile::start_decode(), url='" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GThread * thread_to_delete=0; + flags.enter(); + G_TRY { + if (!(flags & DONT_START_DECODE) && !is_decoding()) + { + if (flags & DECODE_STOPPED) reset(); + flags&=~(DECODE_OK | DECODE_STOPPED | DECODE_FAILED); + flags|=DECODING; + + // Don't delete the thread while you're owning the flags lock + // Beware of deadlock! + thread_to_delete=decode_thread; decode_thread=0; + + // We want to create it right here to be able to stop the + // decoding thread even before its function is called (it starts) + decode_data_pool=DataPool::create(data_pool); + decode_life_saver=this; + + decode_thread=new GThread(); + decode_thread->create(static_decode_func, this); + } + } + G_CATCH_ALL + { + flags&=~DECODING; + flags|=DECODE_FAILED; + flags.leave(); + get_portcaster()->notify_file_flags_changed(this, DECODE_FAILED, DECODING); + delete thread_to_delete; + G_RETHROW; + } + G_ENDCATCH; + flags.leave(); + delete thread_to_delete; +} + +bool +DjVuFile::resume_decode(const bool sync) +{ + bool retval=false; + { + GMonitorLock lock(&flags); + if( !is_decoding() && !is_decode_ok() && !is_decode_failed() ) + { + start_decode(); + retval=true; + } + } + if(sync) + { + wait_for_finish(); + } + return retval; +} + +void +DjVuFile::stop_decode(bool sync) +{ + check(); + + DEBUG_MSG("DjVuFile::stop_decode(), url='" << url << + "', sync=" << (int) sync << "\n"); + DEBUG_MAKE_INDENT(3); + + G_TRY + { + flags|=DONT_START_DECODE; + + // Don't stop SYNCHRONOUSLY from the thread where the decoding is going!!! + { + // First - ask every included child to stop in async mode + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->stop_decode(0); + +// if (decode_data_pool) decode_data_pool->stop(); + } + + if (sync) + { + while(1) + { + GP<DjVuFile> file; + { + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + { + GP<DjVuFile> & f=inc_files_list[pos]; + if (f->is_decoding()) + { + file=f; break; + } + } + } + if (!file) break; + + file->stop_decode(1); + } + + wait_for_finish(1); // Wait for self termination + + // Don't delete the thread here. Until GPBase::preserve() is + // reimplemented somehow at the GThread level. + // delete decode_thread; decode_thread=0; + } + flags&=~(DONT_START_DECODE); + } G_CATCH_ALL { + flags&=~(DONT_START_DECODE); + G_RETHROW; + } G_ENDCATCH; +} + +void +DjVuFile::stop(bool only_blocked) +// This is a one-way function. There is no way to undo the stop() +// command. +{ + DEBUG_MSG("DjVuFile::stop(): Stopping everything\n"); + DEBUG_MAKE_INDENT(3); + + flags|=only_blocked ? BLOCKED_STOPPED : STOPPED; + if (data_pool) data_pool->stop(only_blocked); + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->stop(only_blocked); +} + +GP<DjVuNavDir> +DjVuFile::find_ndir(GMap<GURL, void *> & map) +{ + check(); + + DEBUG_MSG("DjVuFile::find_ndir(): looking for NDIR in '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (dir) return dir; + + if (!map.contains(url)) + { + map[url]=0; + + GPList<DjVuFile> list=get_included_files(false); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuNavDir> d=list[pos]->find_ndir(map); + if (d) return d; + } + } + return 0; +} + +GP<DjVuNavDir> +DjVuFile::find_ndir(void) +{ + GMap<GURL, void *> map; + return find_ndir(map); +} + +GP<DjVuNavDir> +DjVuFile::decode_ndir(GMap<GURL, void *> & map) +{ + check(); + + DEBUG_MSG("DjVuFile::decode_ndir(): decoding for NDIR in '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (dir) return dir; + + if (!map.contains(url)) + { + map[url]=0; + + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid=="NDIR") + { + GP<DjVuNavDir> d=DjVuNavDir::create(url); + d->decode(*iff.get_bytestream()); + dir=d; + break; + } + iff.seek_close_chunk(); + } + if ((!dir)&&(chunks_number < 0)) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + data_pool->clear_stream(); + if (dir) return dir; + + GPList<DjVuFile> list=get_included_files(false); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuNavDir> d=list[pos]->decode_ndir(map); + if (d) return d; + } + data_pool->clear_stream(); + } + return 0; +} + +GP<DjVuNavDir> +DjVuFile::decode_ndir(void) +{ + GMap<GURL, void *> map; + return decode_ndir(map); +} + +void +DjVuFile::get_merged_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &gstr_out, const GList<GURL> & ignore_list, + int level, int & max_level, GMap<GURL, void *> & map) +{ + DEBUG_MSG("DjVuFile::get_merged_anno()\n"); + GURL url=file->get_url(); + if (!map.contains(url)) + { + ByteStream &str_out=*gstr_out; + map[url]=0; + + // Do the included files first (To make sure that they have + // less precedence) + // Depending on if we have all data present, we will + // either create all included files or will use only + // those that have already been created + GPList<DjVuFile> list=file->get_included_files(!file->is_data_present()); + for(GPosition pos=list;pos;++pos) + get_merged_anno(list[pos], gstr_out, ignore_list, level+1, max_level, map); + + // Now process the DjVuFile's own annotations + if (!ignore_list.contains(file->get_url())) + { + if (!file->is_data_present() || + file->is_modified() && file->anno) + { + // Process the decoded (?) anno + GCriticalSectionLock lock(&file->anno_lock); + if (file->anno && file->anno->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->anno->seek(0); + str_out.copy(*file->anno); + } + } else if (file->is_data_present()) + { + // Copy all annotations chunks, but do NOT modify + // this->anno (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str(file->data_pool->get_stream()); + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + while(iff.get_chunk(chkid)) + { + if (chkid=="FORM:ANNO") + { + if (max_level<level) + max_level=level; + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + str_out.copy(*iff.get_bytestream()); + } + else if (is_annotation(chkid)) // but not FORM:ANNO + { + if (max_level<level) + max_level=level; + if (str_out.tell()&&chkid != "ANTz") + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + file->data_pool->clear_stream(); + } + } + } +} + +GP<ByteStream> +DjVuFile::get_merged_anno(const GList<GURL> & ignore_list, + int * max_level_ptr) + // Will do the same thing as get_merged_anno(int *), but will + // ignore DjVuFiles with URLs from the ignore_list +{ + DEBUG_MSG("DjVuFile::get_merged_anno()\n"); + GP<ByteStream> gstr(ByteStream::create()); + GMap<GURL, void *> map; + int max_level=0; + get_merged_anno(this, gstr, ignore_list, 0, max_level, map); + if (max_level_ptr) + *max_level_ptr=max_level; + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_merged_anno(int * max_level_ptr) +// Will go down the DjVuFile's hierarchy and decode all DjVuAnno even +// when the DjVuFile is not fully decoded yet. To avoid correlations +// with DjVuFile::decode(), we do not modify DjVuFile::anno data. +// +// Files deeper in the hierarchy have less influence on the +// results. It means, for example, that the if annotations are +// specified in the top level page file and in a shared file, +// the top level page file settings will take precedence. +// +// NOTE! This function guarantees correct results only if the +// DjVuFile has all data +{ + GList<GURL> ignore_list; + return get_merged_anno(ignore_list, max_level_ptr); +} + + +// [LB->BCR] The following six functions get_anno, get_text, get_meta +// contain the same code in triplicate!!! + +void +DjVuFile::get_anno( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_anno()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->anno) + { + // Process the decoded (?) anno + GCriticalSectionLock lock(&file->anno_lock); + if (file->anno && file->anno->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->anno->seek(0); + str_out.copy(*file->anno); + } + } else if (file->is_data_present()) + { + // Copy all anno chunks, but do NOT modify + // DjVuFile::anno (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_annotation(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +void +DjVuFile::get_text( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_text()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->text) + { + // Process the decoded (?) text + GCriticalSectionLock lock(&file->text_lock); + if (file->text && file->text->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->text->seek(0); + str_out.copy(*file->text); + } + } else if (file->is_data_present()) + { + // Copy all text chunks, but do NOT modify + // DjVuFile::text (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_text(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +void +DjVuFile::get_meta( + const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out) +{ + DEBUG_MSG("DjVuFile::get_meta()\n"); + ByteStream &str_out=*gstr_out; + if (!file->is_data_present() || + file->is_modified() && file->meta) + { + // Process the decoded (?) meta + GCriticalSectionLock lock(&file->meta_lock); + if (file->meta && file->meta->size()) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + file->meta->seek(0); + str_out.copy(*file->meta); + } + } else if (file->is_data_present()) + { + // Copy all meta chunks, but do NOT modify + // DjVuFile::meta (to avoid correlation with DjVuFile::decode()) + const GP<ByteStream> str=file->data_pool->get_stream(); + const GP<IFFByteStream> giff=IFFByteStream::create(str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + if (iff.get_chunk(chkid)) + { + while(iff.get_chunk(chkid)) + { + if (is_meta(chkid)) + { + if (str_out.tell()) + { + str_out.write((void *) "", 1); + } + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + iff_out.copy(*iff.get_bytestream()); + iff_out.close_chunk(); + } + iff.close_chunk(); + } + } + file->data_pool->clear_stream(); + } +} + +GP<ByteStream> +DjVuFile::get_anno(void) +{ + DEBUG_MSG("DjVuFile::get_text(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_anno(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_text(void) +{ + DEBUG_MSG("DjVuFile::get_text(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_text(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +GP<ByteStream> +DjVuFile::get_meta(void) +{ + DEBUG_MSG("DjVuFile::get_meta(void)\n"); + GP<ByteStream> gstr(ByteStream::create()); + get_meta(this, gstr); + ByteStream &str=*gstr; + if (!str.tell()) + { + gstr=0; + }else + { + str.seek(0); + } + return gstr; +} + +void +DjVuFile::static_trigger_cb(void * cl_data) +{ + DjVuFile * th=(DjVuFile *) cl_data; + G_TRY { + GP<DjVuPort> port=DjVuPort::get_portcaster()->is_port_alive(th); + if (port && port->inherits("DjVuFile")) + ((DjVuFile *) (DjVuPort *) port)->trigger_cb(); + } G_CATCH(exc) { + G_TRY { + get_portcaster()->notify_error(th, exc.get_cause()); + } G_CATCH_ALL {} G_ENDCATCH; + } G_ENDCATCH; +} + +void +DjVuFile::trigger_cb(void) +{ + GP<DjVuFile> life_saver=this; + + DEBUG_MSG("DjVuFile::trigger_cb(): got data for '" << url << "'\n"); + DEBUG_MAKE_INDENT(3); + + file_size=data_pool->get_length(); + flags|=DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, DATA_PRESENT, 0); + + if (!are_incl_files_created()) + process_incl_chunks(); + + bool all=true; + inc_files_lock.lock(); + GPList<DjVuFile> files_list=inc_files_list; + inc_files_lock.unlock(); + for(GPosition pos=files_list;pos&&(all=files_list[pos]->is_all_data_present());++pos) + EMPTY_LOOP; + if (all) + { + DEBUG_MSG("DjVuFile::trigger_cb(): We have ALL data for '" << url << "'\n"); + flags|=ALL_DATA_PRESENT; + get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0); + } +} + +void +DjVuFile::progress_cb(int pos, void * cl_data) +{ + DEBUG_MSG("DjVuFile::progress_cb() called\n"); + DEBUG_MAKE_INDENT(3); + + DjVuFile * th=(DjVuFile *) cl_data; + + int length=th->decode_data_pool->get_length(); + if (length>0) + { + float progress=(float) pos/length; + DEBUG_MSG("progress=" << progress << "\n"); + get_portcaster()->notify_decode_progress(th, progress); + } else + { + DEBUG_MSG("DataPool size is still unknown => ignoring\n"); + } +} + +//***************************************************************************** +//******************************** Utilities ********************************** +//***************************************************************************** + +void +DjVuFile::move(GMap<GURL, void *> & map, const GURL & dir_url) +// This function may block for data. +{ + if (!map.contains(url)) + { + map[url]=0; + + url=GURL::UTF8(url.name(),dir_url); + + + // Leave the lock here! + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;++pos) + inc_files_list[pos]->move(map, dir_url); + } +} + +void +DjVuFile::move(const GURL & dir_url) +// This function may block for data. +{ + check(); + DEBUG_MSG("DjVuFile::move(): dir_url='" << dir_url << "'\n"); + DEBUG_MAKE_INDENT(3); + + GMap<GURL, void *> map; + move(map, dir_url); +} + +void +DjVuFile::set_name(const GUTF8String &name) +{ + DEBUG_MSG("DjVuFile::set_name(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + url=GURL::UTF8(name,url.base()); +} + +//***************************************************************************** +//****************************** Data routines ******************************** +//***************************************************************************** + +int +DjVuFile::get_chunks_number(void) +{ + if(chunks_number < 0) + { + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chksize; + for(;(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + iff.seek_close_chunk(); + } + chunks_number=last_chunk; + } + G_CATCH(ex) + { + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + } + G_ENDCATCH; + data_pool->clear_stream(); + } + return chunks_number; +} + +GUTF8String +DjVuFile::get_chunk_name(int chunk_num) +{ + if(chunk_num < 0) + { + G_THROW( ERR_MSG("DjVuFile.illegal_chunk") ); + } + if((chunks_number >= 0)&&(chunk_num > chunks_number)) + { + G_THROW( ERR_MSG("DjVuFile.missing_chunk") ); + } + check(); + + GUTF8String name; + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + if (chunks++==chunk_num) { name=chkid; break; } + iff.seek_close_chunk(); + } + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + if (!name.length()) + { + if (chunks_number < 0) chunks_number=chunks; + G_THROW( ERR_MSG("DjVuFile.missing_chunk") ); + } + return name; +} + +bool +DjVuFile::contains_chunk(const GUTF8String &chunk_name) +{ + check(); + DEBUG_MSG("DjVuFile::contains_chunk(): url='" << url << "', chunk_name='" << + chunk_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + bool contains=0; + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF((recover_errors<=SKIP_PAGES)) + + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (chkid==chunk_name) { contains=1; break; } + iff.seek_close_chunk(); + } + if (!contains &&(chunks_number < 0)) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors <= SKIP_PAGES)); + } + G_ENDCATCH; + data_pool->clear_stream(); + return contains; +} + +bool +DjVuFile::contains_anno(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_annotation(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +bool +DjVuFile::contains_text(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_text(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +bool +DjVuFile::contains_meta(void) +{ + const GP<ByteStream> str(data_pool->get_stream()); + + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + while(iff.get_chunk(chkid)) + { + if (is_meta(chkid)) + return true; + iff.close_chunk(); + } + + data_pool->clear_stream(); + return false; +} + +//***************************************************************************** +//****************************** Save routines ******************************** +//***************************************************************************** + +static void +copy_chunks(const GP<ByteStream> &from, IFFByteStream &ostr) +{ + from->seek(0); + const GP<IFFByteStream> giff(IFFByteStream::create(from)); + IFFByteStream &iff=*giff; + GUTF8String chkid; + int chksize; + while ((chksize=iff.get_chunk(chkid))) + { + ostr.put_chunk(chkid); + int ochksize=ostr.copy(*iff.get_bytestream()); + ostr.close_chunk(); + iff.seek_close_chunk(); + if(ochksize != chksize) + { + G_THROW( ByteStream::EndOfFile ); + } + } +} + + +void +DjVuFile::add_djvu_data(IFFByteStream & ostr, GMap<GURL, void *> & map, + const bool included_too, const bool no_ndir) +{ + check(); + if (map.contains(url)) return; + bool top_level = !map.size(); + map[url]=0; + bool processed_annotation = false; + bool processed_text = false; + bool processed_meta = false; + + const GP<ByteStream> str(data_pool->get_stream()); + GUTF8String chkid; + const GP<IFFByteStream> giff(IFFByteStream::create(str)); + IFFByteStream &iff=*giff; + if (!iff.get_chunk(chkid)) + REPORT_EOF(true) + + // Open toplevel form + if (top_level) + ostr.put_chunk(chkid); + // Process chunks + int chunks=0; + int last_chunk=0; + G_TRY + { + int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1); + int chksize; + for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks) + { + chunks++; + if (is_info(chkid) && info) + { + ostr.put_chunk(chkid); + info->encode(*ostr.get_bytestream()); + ostr.close_chunk(); + } + else if (chkid=="INCL" && included_too) + { + GP<DjVuFile> file = process_incl_chunk(*iff.get_bytestream()); + if (file) + { + if(recover_errors!=ABORT) + file->set_recover_errors(recover_errors); + if(verbose_eof) + file->set_verbose_eof(verbose_eof); + file->add_djvu_data(ostr, map, included_too, no_ndir); + } + } + else if (is_annotation(chkid) && anno && anno->size()) + { + if (!processed_annotation) + { + processed_annotation = true; + GCriticalSectionLock lock(&anno_lock); + copy_chunks(anno, ostr); + } + } + else if (is_text(chkid) && text && text->size()) + { + if (!processed_text) + { + processed_text = true; + GCriticalSectionLock lock(&text_lock); + copy_chunks(text, ostr); + } + } + else if (is_meta(chkid) && meta && meta->size()) + { + if (!processed_meta) + { + processed_meta = true; + GCriticalSectionLock lock(&meta_lock); + copy_chunks(meta, ostr); + } + } + else if (chkid!="NDIR"||!(no_ndir || dir)) + { // Copy NDIR chunks, but never generate new ones. + ostr.put_chunk(chkid); + ostr.copy(*iff.get_bytestream()); + ostr.close_chunk(); + } + iff.seek_close_chunk(); + } + if (chunks_number < 0) chunks_number=last_chunk; + } + G_CATCH(ex) + { + if(!ex.cmp_cause(ByteStream::EndOfFile)) + { + if (chunks_number < 0) + chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk; + report_error(ex,(recover_errors<=SKIP_PAGES)); + }else + { + report_error(ex,true); + } + } + G_ENDCATCH; + + // Otherwise, writes annotation at the end (annotations could be big) + if (!processed_annotation && anno && anno->size()) + { + processed_annotation = true; + GCriticalSectionLock lock(&anno_lock); + copy_chunks(anno, ostr); + } + if (!processed_text && text && text->size()) + { + processed_text = true; + GCriticalSectionLock lock(&text_lock); + copy_chunks(text, ostr); + } + if (!processed_meta && meta && meta->size()) + { + processed_meta = true; + GCriticalSectionLock lock(&meta_lock); + copy_chunks(meta, ostr); + } + // Close iff + if (top_level) + ostr.close_chunk(); + + data_pool->clear_stream(); +} + +GP<ByteStream> +DjVuFile::get_djvu_bytestream(const bool included_too, const bool no_ndir) +{ + check(); + DEBUG_MSG("DjVuFile::get_djvu_bytestream(): creating DjVu raw file\n"); + DEBUG_MAKE_INDENT(3); + const GP<ByteStream> pbs(ByteStream::create()); + const GP<IFFByteStream> giff=IFFByteStream::create(pbs); + IFFByteStream &iff=*giff; + GMap<GURL, void *> map; + add_djvu_data(iff, map, included_too, no_ndir); + iff.flush(); + pbs->seek(0, SEEK_SET); + return pbs; +} + +GP<DataPool> +DjVuFile::get_djvu_data(const bool included_too, const bool no_ndir) +{ + const GP<ByteStream> pbs = get_djvu_bytestream(included_too, no_ndir); + return DataPool::create(pbs); +} + +void +DjVuFile::merge_anno(ByteStream &out) +{ + // Reuse get_merged_anno(), which is better than the previous + // implementation due to three things: + // 1. It works even before the file is completely decoded + // 2. It merges annotations taking into account where a child DjVuFile + // is included. + // 3. It handles loops in DjVuFile's hierarchy + + const GP<ByteStream> str(get_merged_anno()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + +void +DjVuFile::get_text(ByteStream &out) +{ + const GP<ByteStream> str(get_text()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + +void +DjVuFile::get_meta(ByteStream &out) +{ + const GP<ByteStream> str(get_meta()); + if (str) + { + str->seek(0); + if (out.tell()) + { + out.write((void *) "", 1); + } + out.copy(*str); + } +} + + + +//**************************************************************************** +//******************************* Modifying ********************************** +//**************************************************************************** + +void +DjVuFile::remove_anno(void) +{ + DEBUG_MSG("DjVuFile::remove_anno()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_annotation(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + anno=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::remove_text(void) +{ + DEBUG_MSG("DjVuFile::remove_text()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_text(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + text=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::remove_meta(void) +{ + DEBUG_MSG("DjVuFile::remove_meta()\n"); + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<ByteStream> gstr_out(ByteStream::create()); + + GUTF8String chkid; + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + if (!iff_in.get_chunk(chkid)) + G_THROW( ByteStream::EndOfFile ); + + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + iff_out.put_chunk(chkid); + + while(iff_in.get_chunk(chkid)) + { + if (!is_meta(chkid)) + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + + iff_out.close_chunk(); + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + meta=0; + + flags|=MODIFIED; + data_pool->clear_stream(); +} + +void +DjVuFile::rebuild_data_pool(void) +{ + data_pool=get_djvu_data(false,false); + chunks_number=1; + flags|=MODIFIED; +} + +// Do NOT comment this function out. It's used by DjVuDocEditor to convert +// old-style DjVu documents to BUNDLED format. + +GP<DataPool> +DjVuFile::unlink_file(const GP<DataPool> & data, const GUTF8String &name) +// Will process contents of data[] and remove any INCL chunk +// containing 'name' +{ + DEBUG_MSG("DjVuFile::unlink_file()\n"); + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + const GP<ByteStream> str_in(data->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + int chksize; + GUTF8String chkid; + if (!iff_in.get_chunk(chkid)) return data; + + iff_out.put_chunk(chkid); + + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid=="INCL") + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + { + incl_str.setat(incl_str.length()-1, 0); + } + if (incl_str!=name) + { + iff_out.put_chunk(chkid); + iff_out.get_bytestream()->writestring(incl_str); + iff_out.close_chunk(); + } + } else + { + iff_out.put_chunk(chkid); + char buffer[1024]; + int length; + for(const GP<ByteStream> gbs(iff_out.get_bytestream()); + (length=iff_in.read(buffer, 1024));) + { + gbs->writall(buffer, length); + } + iff_out.close_chunk(); + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + iff_out.flush(); + gstr_out->seek(0, SEEK_SET); + data->clear_stream(); + return DataPool::create(gstr_out); +} + +#ifndef NEED_DECODER_ONLY +void +DjVuFile::insert_file(const GUTF8String &id, int chunk_num) +{ + DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "', chunk_num=" + << chunk_num << "\n"); + DEBUG_MAKE_INDENT(3); + + // First: create new data + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + int chunk_cnt=0; + bool done=false; + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + int chksize; + while((chksize=iff_in.get_chunk(chkid))) + { + if (chunk_cnt++==chunk_num) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + done=true; + } + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + iff_in.close_chunk(); + } + if (!done) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(id); + iff_out.close_chunk(); + } + iff_out.close_chunk(); + } + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + // Second: create missing DjVuFiles + process_incl_chunks(); + + flags|=MODIFIED; + data_pool->clear_stream(); +} +#endif + +void +DjVuFile::unlink_file(const GUTF8String &id) +{ + DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "'\n"); + DEBUG_MAKE_INDENT(3); + + // Remove the file from the list of included files + { + GURL url=DjVuPort::get_portcaster()->id_to_url(this, id); + if (url.is_empty()) url=GURL::UTF8(id,this->url.base()); + GCriticalSectionLock lock(&inc_files_lock); + for(GPosition pos=inc_files_list;pos;) + if (inc_files_list[pos]->get_url()==url) + { + GPosition this_pos=pos; + ++pos; + inc_files_list.del(this_pos); + } else ++pos; + } + + // And update the data. + const GP<ByteStream> str_in(data_pool->get_stream()); + const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in)); + IFFByteStream &iff_in=*giff_in; + + const GP<ByteStream> gstr_out(ByteStream::create()); + const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out)); + IFFByteStream &iff_out=*giff_out; + + GUTF8String chkid; + if (iff_in.get_chunk(chkid)) + { + iff_out.put_chunk(chkid); + int chksize; + while((chksize=iff_in.get_chunk(chkid))) + { + if (chkid!="INCL") + { + iff_out.put_chunk(chkid); + iff_out.copy(*iff_in.get_bytestream()); + iff_out.close_chunk(); + } else + { + GUTF8String incl_str; + char buffer[1024]; + int length; + while((length=iff_in.read(buffer, 1024))) + incl_str+=GUTF8String(buffer, length); + + // Eat '\n' in the beginning and at the end + while(incl_str.length() && incl_str[0]=='\n') + { + incl_str=incl_str.substr(1,(unsigned int)(-1)); + } + while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n') + incl_str.setat(incl_str.length()-1, 0); + if (incl_str!=id) + { + iff_out.put_chunk("INCL"); + iff_out.get_bytestream()->writestring(incl_str); + iff_out.close_chunk(); + } + } + iff_in.close_chunk(); + } + iff_out.close_chunk(); + } + + gstr_out->seek(0, SEEK_SET); + data_pool=DataPool::create(gstr_out); + chunks_number=-1; + + flags|=MODIFIED; +} + +void +DjVuFile::change_info(GP<DjVuInfo> xinfo,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_text()\n"); + // Mark this as modified + set_modified(true); + if(do_reset) + reset(); + info=xinfo; +} + +#ifndef NEED_DECODER_ONLY +void +DjVuFile::change_text(GP<DjVuTXT> txt,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_text()\n"); + GP<DjVuText> gtext_c=DjVuText::create(); + DjVuText &text_c=*gtext_c; + if(contains_text()) + { + const GP<ByteStream> file_text(get_text()); + if(file_text) + { + text_c.decode(file_text); + } + } + GCriticalSectionLock lock(&text_lock); + // Mark this as modified + set_modified(true); + if(do_reset) + reset(); + text_c.txt = txt; + text=ByteStream::create(); + text_c.encode(text); +} + +void +DjVuFile::change_meta(const GUTF8String &xmeta,const bool do_reset) +{ + DEBUG_MSG("DjVuFile::change_meta()\n"); + // Mark this as modified + set_modified(true); + if(contains_meta()) + { + (void)get_meta(); + } + if(do_reset) + reset(); + GCriticalSectionLock lock(&meta_lock); + meta=ByteStream::create(); + if(xmeta.length()) + { + const GP<IFFByteStream> giff=IFFByteStream::create(meta); + IFFByteStream &iff=*giff; + iff.put_chunk("METz"); + { + GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50); + gbsiff->writestring(xmeta); + } + iff.close_chunk(); + } +} +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFile.h b/kviewshell/plugins/djvu/libdjvu/DjVuFile.h new file mode 100644 index 00000000..ea0e6db3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFile.h @@ -0,0 +1,848 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFile.h,v 1.9 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUFILE_H +#define _DJVUFILE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuInfo.h" +#include "DjVuPalette.h" +#include "DjVuPort.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DjVuTXT; +class ByteStream; +class DataPool; +class JB2Image; +class JB2Dict; +class IW44Image; +class IFFByteStream; +class GPixmap; +class DjVuNavDir; + + +/** @name DjVuFile.h + Files #"DjVuFile.h"# and #"DjVuFile.cpp"# contain implementation of the + \Ref{DjVuFile} class, which takes the leading role in decoding of + \Ref{DjVuImage}s. + + In the previous releases of the library the work of decoding has been + entirely done in \Ref{DjVuImage}. Now, due to the introduction of multipage + documents, the decoding procedure became significantly more complex and + has been moved out from \Ref{DjVuImage} into \Ref{DjVuFile}. + + There is not much point though in creating just \Ref{DjVuFile} alone. + The maximum power of the decoder is achieved when you create the + \Ref{DjVuDocument} and work with {\bf it} when decoding the image. + + @memo Classes representing DjVu files. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVuFile.h,v 1.9 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuFile# plays the central role in decoding \Ref{DjVuImage}s. + First of all, it represents a DjVu file whether it's part of a + multipage all-in-one-file DjVu document, or part of a multipage + DjVu document where every page is in a separate file, or the whole + single page document. #DjVuFile# can read its contents from a file + and store it back when necessary. + + Second, #DjVuFile# does the greatest part of decoding work. In the + past this was the responsibility of \Ref{DjVuImage}. Now, with the + introduction of the multipage DjVu formats, the decoding routines + have been extracted from the \Ref{DjVuImage} and put into this separate + class #DjVuFile#. + + As \Ref{DjVuImage} before, #DjVuFile# now contains public class + variables corresponding to every component, that can ever be decoded + from a DjVu file (such as #INFO# chunk, #BG44# chunk, #SJBZ# chunk, etc.). + + As before, the decoding is initiated by a single function + (\Ref{start_decode}() in this case, and \Ref{DjVuImage::decode}() before). + The difference is that #DjVuFile# now handles threads creation itself. + When you call the \Ref{start_decode}() function, it creates the decoding + thread, which starts decoding, and which can create additional threads: + one per each file included into this one. + + {\bf Inclusion} is also a new feature specifically designed for a + multipage document. Indeed, inside a given document there can be a lot + of things shared between its pages. Examples can be the document + annotation (\Ref{DjVuAnno}) and other things like shared shapes and + dictionary (to be implemented). To avoid putting these chunks into + every page, we have invented new chunk called #INCL# which purpose is + to make the decoder open the specified file and decode it. + + {\bf Source of data.} The #DjVuFile# can be initialized in two ways: + \begin{itemize} + \item With #URL# and \Ref{DjVuPort}. In this case #DjVuFile# will + request its data thru the communication mechanism provided by + \Ref{DjVuPort} in the constructor. If this file references + (includes) any other file, data for them will also be requested + in the same way. + \item With \Ref{ByteStream}. In this case the #DjVuFile# will read + its data directly from the passed stream. This constructor + has been added to simplify creation of #DjVuFile#s, which do + no include anything else. In this case the \Ref{ByteStream} + is enough for the #DjVuFile# to initialize. + \end{itemize} + + {\bf Progress information.} #DjVuFile# does not do decoding silently. + Instead, it sends a whole set of notifications through the mechanism + provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. It tells the user + of the class about the progress of the decoding, about possible errors, + chunk being decoded, etc. The data is requested using this mechanism too. + + {\bf Creating.} Depending on where you have data of the DjVu file, the + #DjVuFile# can be initialized in two ways: + \begin{itemize} + \item By providing #URL# and pointer to \Ref{DjVuPort}. In this case + #DjVuFile# will request data using communication mechanism + provided by \Ref{DjVuPort}. This is useful when the data is on + the web or when this file includes other files. + \item By providing a \Ref{ByteStream} with the data for the file. Use + it only when the file doesn't include other files. + \end{itemize} + There is also a bunch of functions provided for composing + the desired \Ref{DjVuDocument} and modifying #DjVuFile# structure. The + examples are \Ref{delete_chunks}(), \Ref{insert_chunk}(), + \Ref{include_file}() and \Ref{unlink_file}(). + + {\bf Caching.} In the case of plugin it's important to do the caching + of decoded images or files. #DjVuFile# appears to be the best candidate + for caching, and that's why it supports this procedure. Whenever a + #DjVuFile# is successfully decoded, it's added to the cache by + \Ref{DjVuDocument}. Next time somebody needs it, it will be extracted + from the cache directly by \Ref{DjVuDocument} and won't be decoded again. + + {\bf URLs.} Historically the biggest strain is put on making the decoder + available for Netscape and IE plugins where the original files reside + somewhere in the net. That is why #DjVuFile# uses {\bf URLs} to + identify itself and other files. If you're working with files on the + hard disk, you have to use the local URLs instead of file names. + A good way to do two way conversion is the \Ref{GOS} class. Sometimes it + happens that a given file does not reside anywhere but the memory. No + problem in this case either. There is a special port \Ref{DjVuMemoryPort}, + which can associate any URL with the corresponding data in the memory. + All you need to do is to invent your own URL prefix for this case. + "#memory:#" will do. The usage of absolute URLs has many advantages among + which is the capability to cache files with their URL being the cache key. + + Please note, that the #DjVuFile# class has been designed to work closely + with \Ref{DjVuDocument}. So please review the documentation on this class + too. */ + +class DjVuFile : public DjVuPort +{ +public: + enum { DECODING=1, DECODE_OK=2, DECODE_FAILED=4, DECODE_STOPPED=8, + DATA_PRESENT=16, ALL_DATA_PRESENT=32, INCL_FILES_CREATED=64, + MODIFIED=128, DONT_START_DECODE=256, STOPPED=512, + BLOCKED_STOPPED=1024, CAN_COMPRESS=2048, NEEDS_COMPRESSION=4096 }; + enum { STARTED=1, FINISHED=2 }; + + /** @name Decoded file contents */ + //@{ + /// Pointer to the DjVu file information component. + GP<DjVuInfo> info; + /// Pointer to the background component of DjVu image (IW44 encoded). + GP<IW44Image> bg44; + /// Pointer to the background component of DjVu image (Raw). + GP<GPixmap> bgpm; + /// Pointer to the mask of foreground component of DjVu image (JB2 encoded). + GP<JB2Image> fgjb; + /// Pointer to the optional shape dictionary for the mask (JB2 encoded). + GP<JB2Dict> fgjd; + /// Pointer to a colors layer for the foreground component of DjVu image. + GP<GPixmap> fgpm; + /// Pointer to a colors vector for the foreground component of DjVu image. + GP<DjVuPalette> fgbc; + /// Pointer to collected annotation chunks. + GP<ByteStream> anno; + /// Pointer to collected hiddentext chunks. + GP<ByteStream> text; + /// Pointer to meta data chunks. + GP<ByteStream> meta; + /// Pointer to the *old* navigation directory contained in this file + GP<DjVuNavDir> dir; + /// Description of the file formed during decoding + GUTF8String description; + /// MIME type string describing the DjVu data. + GUTF8String mimetype; + /// Size of the file. + int file_size; + //@} + +protected: + /** Default constructor. Must follow with an init() */ + DjVuFile(void); +public: + virtual ~DjVuFile(void); + + /** Initializes a #DjVuFile# object. This is a simplified initializer, + which is not supposed to be used for decoding or creating + #DjVuFile#s, which include other files. + + If the file is stored on the hard drive, you may also use the + other constructor and pass it the file's URL and #ZERO# #port#. + The #DjVuFile# will read the data itself. + + If you want to receive error messages and notifications, you + may connect the #DjVuFile# to your own \Ref{DjVuPort} after + it has been constructed. + + @param str The stream containing data for the file. */ + void init(const GP<ByteStream> & str); + + /** Creator, does the init(ByteStream &str) */ + static GP<DjVuFile> create( const GP<ByteStream> & str, + const ErrorRecoveryAction recover_action=ABORT, + const bool verbose_eof=true); + + /** Initializes a #DjVuFile# object. As you can notice, the data is not + directly passed to this function. The #DjVuFile# will ask for it + through the \Ref{DjVuPort} mechanism before the constructor + finishes. If the data is stored locally on the hard disk then the + pointer to \Ref{DjVuPort} may be set to #ZERO#, which will make + #DjVuFile# read all data from the hard disk and report all errors + to #stderr#. + + {\bf Note}. If the file includes (by means of #INCL# chunks) other + files then you should be ready to + \begin{enumerate} + \item Reply to requests \Ref{DjVuPort::id_to_url}() issued to + translate IDs (used in #INCL# chunks) to absolute URLs. + Usually, when the file is created by \Ref{DjVuDocument} + this job is done by it. If you construct such a file + manually, be prepared to do the ID to URL translation + \item Provide data for all included files. + \end{enumerate} + + @param url The URL assigned to this file. It will be used when + the #DjVuFile# asks for data. + @param port All communication between #DjVuFile#s and \Ref{DjVuDocument}s + is done through the \Ref{DjVuPort} mechanism. If the {\em url} + is not local or the data does not reside on the hard disk, + the {\em port} parameter must not be #ZERO#. If the {\em port} + is #ZERO# then #DjVuFile# will create an internal instance + of \Ref{DjVuSimplePort} for accessing local files and + reporting errors. It can later be disabled by means + of \Ref{disable_standard_port}() function. */ + void init(const GURL & url, GP<DjVuPort> port=0); + + /** Creator, does the init(const GURL &url, GP<DjVuPort> port=0) */ + static GP<DjVuFile> create( + const GURL & url, GP<DjVuPort> port=0, + const ErrorRecoveryAction recover_action=ABORT, + const bool verbose_eof=true); + + /** Disables the built-in port for accessing local files, which may + have been created in the case when the #port# argument to + the \Ref{DjVuFile::DjVuFile}() constructor is #ZERO# */ + void disable_standard_port(void); + + /** Looks for #decoded# navigation directory (\Ref{DjVuNavDir}) in this + or included files. Returns #ZERO# if nothing could be found. + + {\bf Note.} This function does {\bf not} attempt to decode #NDIR# + chunks. It is looking for predecoded components. #NDIR# can be + decoded either during regular decoding (initiated by + \Ref{start_decode}() function) or by \Ref{decode_ndir}() function, + which processes this and included files recursively in search + of #NDIR# chunks and decodes them. */ + GP<DjVuNavDir> find_ndir(void); + + /** @name #DjVuFile# flags query functions */ + //@{ + /** Returns the #DjVuFile# flags. The value returned is the + result of ORing one or more of the following constants: + \begin{itemize} + \item #DECODING# The decoding is in progress + \item #DECODE_OK# The decoding has finished successfully + \item #DECODE_FAILED# The decoding has failed + \item #DECODE_STOPPED# The decoding has been stopped by + \Ref{stop_decode}() function + \item #DATA_PRESENT# All data for this file has been received. + It's especially important in the case of Netscape or IE + plugins when the data is being received while the + decoding is done. + \item #ALL_DATA_PRESENT# Not only data for this file, but also + for all included file has been received. + \item #INCL_FILES_CREATED# All #INCL# and #INCF# chunks have been + processed and the corresponding #DjVuFile#s created. This + is important to know to be sure that the list returned by + \Ref{get_included_files}() is OK. + \end{itemize} */ + long get_flags(void) const; + /// Returns #TRUE# if the file is being decoded. + bool is_decoding(void) const; + /// Returns #TRUE# if decoding of the file has finished successfully. + bool is_decode_ok(void) const; + /// Returns #TRUE# if decoding of the file has failed. + bool is_decode_failed(void) const; + /** Returns #TRUE# if decoding of the file has been stopped by + \Ref{stop_decode}() function. */ + bool is_decode_stopped(void) const; + /// Returns #TRUE# if this file has received all data. + bool is_data_present(void) const; + /** Returns #TRUE# if this file {\bf and} all included files have + received all data. */ + bool is_all_data_present(void) const; + /** Returns #TRUE# if all included files have been created. Only when + this function returns 1, the \Ref{get_included_files}() returns + the correct information. */ + bool are_incl_files_created(void) const; + bool is_modified(void) const; + bool needs_compression(void) const; + bool can_compress(void) const; + void set_modified(bool m); + void set_needs_compression(bool m); + void set_can_compress(bool m); + //@} + + /// Returns the URL assigned to this file + GURL get_url(void) const; + + /** @name Decode control routines */ + //@{ + /** Starts decode. If threads are enabled, the decoding will be + done in another thread. Be sure to use \Ref{wait_for_finish}() + or listen for notifications sent through the \Ref{DjVuPortcaster} + to remain in sync. */ + void start_decode(void); + /** Start the decode iff not already decoded. If sync is true, wait + wait for decode to complete. Returns true of start_decode is called. + */ + bool resume_decode(const bool sync=false); + /** Stops decode. If #sync# is 1 then the function will not return + until the decoding thread actually dies. Otherwise it will + just signal the thread to stop and will return immediately. + Decoding of all included files will be stopped too. */ + void stop_decode(bool sync); + /** Recursively stops all data-related operations. + + Depending on the value of #only_blocked# flag this works as follows: + \begin{itemize} + \item If #only_blocked# is #TRUE#, the function will make sure, + that any further access to the file's data will result + in a #STOP# exception if the desired data is not available + (and the thread would normally block). + \item If #only_blocked# is #FALSE#, then {\bf any} further + access to the file's data will result in immediate + #STOP# exception. + \end{itemize} + + The action of this function is recursive, meaning that any #DjVuFile# + included into this one will also be stopped. + + Use this function when you don't need the #DjVuFile# anymore. The + results cannot be undone, and the whole idea is to make all threads + working with this file exit with the #STOP# exception. */ + void stop(bool only_blocked); + /** Wait for the decoding to finish. This will wait for the + termination of included files too. */ + void wait_for_finish(void); + /** Looks for #NDIR# chunk (navigation directory), and decodes its + contents. If the #NDIR# chunk has not been found in {\em this} file, + but this file includes others, the procedure will continue + recursively. This function is useful to obtain the document + navigation directory before any page has been decoded. After it + returns the directory can be obtained by calling \Ref{find_ndir}() + function. + + {\bf Warning.} Contrary to \Ref{start_decode}(), this function + does not return before it completely decodes the directory. + Make sure, that this file and all included files have enough data. */ + GP<DjVuNavDir> decode_ndir(void); + /// Clears all decoded components. + void reset(void); + /** Processes #INCL# chunks and creates included files. + Normally you won't need to call this function because included + files are created automatically when the file is being decoded. + But if due to some reason you'd like to obtain the list of included + files without decoding this file, this is an ideal function to call. + + {\bf Warning.} This function does not return before it reads the + whole file, which may block your application under some circumstances + if not all data is available. */ + void process_incl_chunks(void); + //@} + + // Function needed by the cache + unsigned int get_memory_usage(void) const; + + /** Returns the list of included DjVuFiles. + + {\bf Warning.} Included files are normally created during decoding. + Before that they do not exist. If you call this function at + that time and set #only_created# to #FALSE# then it will have to + read all the data from this file in order to find #INCL# chunks, + which may block your application, if not all data is available. + + @param only_created If #TRUE#, the file will not try to process + #INCL# chunks and load referenced files. It will return + just those files, which have already been created during + the decoding procedure. */ + GPList<DjVuFile> get_included_files(bool only_created=true); + + /** Includes a #DjVuFile# with the specified #id# into this one. + This function will also insert an #INCL# chunk at position + #chunk_num#. The function will request data for the included + file and will create it before returning. */ + void insert_file(const GUTF8String &id, int chunk_num=1); + /// Will get rid of included file with the given #id# + void unlink_file(const GUTF8String &id); + /** Will find an #INCL# chunk containing #name# in input #data# and + will remove it */ + static GP<DataPool> unlink_file(const GP<DataPool> & data, const GUTF8String &name); + + /// Returns the number of chunks in the IFF file data + int get_chunks_number(void); + /// Returns the name of chunk number #chunk_num# + GUTF8String get_chunk_name(int chunk_num); + /// Returns 1 if this file contains chunk with name #chunk_name# + bool contains_chunk(const GUTF8String &chunk_name); + + /** Processes the included files hierarchy and returns merged + annotations. This function may be used even when the #DjVuFile# + has not been decoded yet. If all data has been received for + this #DjVuFile# and all included #DjVuFile#s, it will will + gather annotations from them and will return the result. + If no annotations have been found, #ZERO# will be returned. + If either this #DjVuFile# or any of the included files do not + have all the data, the function will use the results of + decoding, which may have been started with the \Ref{start_decode}() + function. Otherwise #ZERO# will be returned as well. + + If #max_level_ptr# pointer is not zero, the function will use + it to store the maximum level number from which annotations + have been obtained. #ZERO# level corresponds to the top-level + page file. + + {\bf Summary:} This function will return complete annotations only + when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_merged_anno(int * max_level_ptr=0); + + /** Returns the annotation chunks (#"ANTa"# and #"ANTz"#). This + function may be used even when the #DjVuFile# has not been decoded + yet. If all data has been received for this #DjVuFile#, it will + gather hidden text and return the result. If no hidden text has + been found, #ZERO# will be returned. + + {\bf Summary:} This function will return complete annotations + only when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_anno(void); + + /** Returns the text chunks (#"TXTa"# and #"TXTz"#). This function may + be used even when the #DjVuFile# has not been decoded yet. If all + data has been received for this #DjVuFile#, it will gather hidden + text and return the result. If no hidden text has been found, + #ZERO# will be returned. + + {\bf Summary:} This function will return complete hidden text layers + only when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_text(void); + + /** Returns the meta chunks (#"METa"# and #"METz"#). This function may + be used even when the #DjVuFile# has not been decoded yet. If all + data has been received for this #DjVuFile#, it will gather metadata + and return the result. If no hidden text has been found, #ZERO# + will be returned. + + {\bf Summary:} This function will return complete meta data only + when the \Ref{is_all_data_present}() returns #TRUE#. */ + GP<ByteStream> get_meta(void); + + /** Goes down the hierarchy of #DjVuFile#s and merges their annotations. + (shouldn't this one be private?). + @param max_level_ptr If this pointer is not ZERO, the function + will use it to store the maximum level at which annotations + were found. Top-level page files have ZERO #level#. + @param ignore_list The function will not process included #DjVuFile#s + with URLs matching those mentioned in this #ignore_list#. */ + GP<ByteStream> get_merged_anno(const GList<GURL> & ignore_list, + int * max_level_ptr); + + /** Clears this file of all annotations. */ + void remove_anno(void); + + /** Clears the hidden text. */ + void remove_text(void); + + /// Clears the meta data. + void remove_meta(void); + + /** Returns #TRUE# if the file contains annotation chunks. + Known annotation chunks at the time of writing this help are: + {\bf ANTa}, {\bf ANTz}, {\bf FORM:ANNO}. */ + bool contains_anno(void); + + /** Returns #TRUE# if the file contains hiddentext chunks. + Known hiddentext chunks at the time of writing this help are: + {\bf TXTa}, and {\bf TXTz}. */ + bool contains_text(void); + + /** Returns #TRUE# if the file contains metadata chunks. + Known metadata chunks at the time of writing this help are: + {\bf METa}, and {\bf METz}. */ + bool contains_meta(void); + + /** Changes the value of the hiddentext. */ + void change_info(GP<DjVuInfo> info, const bool do_reset=false); + + /** Changes the value of the hiddentext. */ + void change_text(GP<DjVuTXT> txt, const bool do_reset=false); + + /** Changes the value of the metadata. */ + void change_meta(const GUTF8String &meta, const bool do_reset=false); + + /** @name Encoding routines */ + //@{ + /** The main function that encodes data back into binary stream. + The data returned will reflect possible changes made into the + chunk structure, annotation chunks and navigation directory + chunk #NDIR#. + + {\bf Note:} The file stream will not have the magic + #0x41,0x54,0x26,0x54# + at the beginning. + + @param included_too Process included files too. */ + GP<ByteStream> get_djvu_bytestream(const bool included_too, const bool no_ndir=true); + + /** Same as \Ref{get_djvu_bytestream}(), returning a DataPool. + @param included_too Process included files too. */ + GP<DataPool> get_djvu_data(const bool included_too, const bool no_ndir=true ); + //@} + + // Internal. Used by DjVuDocument + GP<DataPool> get_init_data_pool(void) const { return data_pool; }; + + // Internal. Used by DjVuDocument. May block for data. + void move(const GURL & dir_url); + + /** Internal. Used by DjVuDocument. The #name# should {\bf not} + be encoded with \Ref{GOS::encode_reserved}(). */ + void set_name(const GUTF8String &name); + + // Internal. Used by DjVuDocument + GSafeFlags & get_safe_flags(void); + + // Internal. Used by DjVuImage + void merge_anno(ByteStream &out); + + // Internal. Used by DjVuImage + void get_text(ByteStream &out); + + // Internal. Used by DjVuImage + void get_meta(ByteStream &out); + + // Internal. Used by DjVuDocEditor + void rebuild_data_pool(void); + + // Functions inherited from DjVuPort + virtual bool inherits(const GUTF8String &class_name) const; + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + virtual void notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask); + virtual void set_recover_errors(const ErrorRecoveryAction=ABORT); + virtual void set_verbose_eof(const bool verbose_eof=true); + virtual void report_error(const GException &ex,const bool=true); + static void set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs)); + +protected: + GURL url; + GP<DataPool> data_pool; + + GPList<DjVuFile> inc_files_list; + GCriticalSection inc_files_lock; + GCriticalSection anno_lock; + GCriticalSection text_lock; + GCriticalSection meta_lock; + ErrorRecoveryAction recover_errors; + bool verbose_eof; + int chunks_number; +private: + bool initialized; + GSafeFlags flags; + + GThread * decode_thread; + GP<DataPool> decode_data_pool; + GP<DjVuFile> decode_life_saver; + + GP<DjVuPort> simple_port; + + GMonitor chunk_mon, finish_mon; + + // Functions called when the decoding thread starts + static void static_decode_func(void *); + void decode_func(void); + void decode(const GP<ByteStream> &str); + GUTF8String decode_chunk(const GUTF8String &chkid, + const GP<ByteStream> &str, bool djvi, bool djvu, bool iw44); + int get_dpi(int w, int h); + + // Functions dealing with the shape directory (fgjd) + static GP<JB2Dict> static_get_fgjd(void *); + GP<JB2Dict> get_fgjd(int block=0); + + // Functions used to wait for smth + void wait_for_chunk(void); + bool wait_for_finish(bool self); + + // INCL chunk processor + GP<DjVuFile> process_incl_chunk(ByteStream & str, int file_num=-1); + + // Trigger: called when DataPool has all data + static void static_trigger_cb(void *); + void trigger_cb(void); + + // Progress callback: called from time to time + static void progress_cb(int pos, void *); + static void get_merged_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out, const GList<GURL> & ignore_list, + int level, int & max_level, GMap<GURL, void *> & map); + static void get_anno(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + static void get_text(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + static void get_meta(const GP<DjVuFile> & file, + const GP<ByteStream> &str_out); + + void check() const; + GP<DjVuNavDir>find_ndir(GMap<GURL, void *> & map); + GP<DjVuNavDir>decode_ndir(GMap<GURL, void *> & map); + void add_djvu_data(IFFByteStream & str, + GMap<GURL, void *> & map, + const bool included_too, const bool no_ndir=true); + void move(GMap<GURL, void *> & map, const GURL & dir_url); +private: // dummy stuff + static void decode(ByteStream *); + static GUTF8String decode_chunk(const GUTF8String &, ByteStream *,bool,bool,bool); + static void get_merged_anno(const GP<DjVuFile> &,ByteStream *, + const GList<GURL> &, int, int &, GMap<GURL, void *> &); + static void get_text(const GP<DjVuFile> &,ByteStream *); + static void get_meta(const GP<DjVuFile> &,ByteStream *); + +}; + +inline long +DjVuFile::get_flags(void) const +{ + return flags; +} + +inline GSafeFlags & +DjVuFile::get_safe_flags(void) +{ + return flags; +} + +inline bool +DjVuFile::is_decoding(void) const +{ + return (flags & DECODING)!=0; +} + +inline bool +DjVuFile::is_decode_ok(void) const +{ + return (flags & DECODE_OK)!=0; +} + +inline bool +DjVuFile::is_decode_failed(void) const +{ + return (flags & DECODE_FAILED)!=0; +} + +inline bool +DjVuFile::is_decode_stopped(void) const +{ + return (flags & DECODE_STOPPED)!=0; +} + +inline bool +DjVuFile::is_data_present(void) const +{ + return (flags & DATA_PRESENT)!=0; +} + +inline bool +DjVuFile::is_all_data_present(void) const +{ + return (flags & ALL_DATA_PRESENT)!=0; +} + +inline bool +DjVuFile::are_incl_files_created(void) const +{ + return (flags & INCL_FILES_CREATED)!=0; +} + +inline bool +DjVuFile::is_modified(void) const +{ + return (flags & MODIFIED)!=0; +} + +inline void +DjVuFile::set_modified(bool m) +{ + flags=m ? (flags | MODIFIED) : (flags & ~MODIFIED); +} + +inline bool +DjVuFile::needs_compression(void) const +{ + return (flags & NEEDS_COMPRESSION)!=0; +} + +inline void +DjVuFile::set_needs_compression(bool m) +{ + if (m) flags=flags | NEEDS_COMPRESSION; + else flags=flags & ~NEEDS_COMPRESSION; +} + +inline bool +DjVuFile::can_compress(void) const +{ + return (flags & CAN_COMPRESS)!=0; +} + +inline void +DjVuFile::set_can_compress(bool m) +{ + if(info) + { + info->compressable=m; + } + if (m) + { + flags=flags | CAN_COMPRESS; + } else + { + flags=flags & ~CAN_COMPRESS; + } +} + +inline void +DjVuFile::disable_standard_port(void) +{ + simple_port=0; +} + +inline bool +DjVuFile::inherits(const GUTF8String &class_name) const +{ + return + (GUTF8String("DjVuFile") == class_name) || + DjVuPort::inherits(class_name); +// !strcmp("DjVuFile", class_name) || +// DjVuPort::inherits(class_name); +} + +inline void +DjVuFile::wait_for_finish(void) +{ + while(wait_for_finish(1)) + EMPTY_LOOP; +} + +inline GURL +DjVuFile::get_url(void) const +{ + return url; +} + +inline void +DjVuFile::set_verbose_eof +(const bool verbose) +{ + verbose_eof=verbose; +} + +inline void +DjVuFile::set_recover_errors +(const ErrorRecoveryAction action) +{ + recover_errors=action; +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp new file mode 100644 index 00000000..13220a96 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp @@ -0,0 +1,272 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFileCache.cpp,v 1.9 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuFileCache.h" +#include "debug.h" + +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +DjVuFileCache::~DjVuFileCache(void) {} + +int +DjVuFileCache::Item::qsort_func(const void * el1, const void * el2) +{ + const Item * item1=*(Item **) el1; + const Item * item2=*(Item **) el2; + time_t time1=item1->get_time(); + time_t time2=item2->get_time(); + return time1<time2 ? -1 : time1>time2 ? 1 : 0; +} + +void +DjVuFileCache::set_max_size(int xmax_size) +{ + DEBUG_MSG("DjVuFileCache::set_max_size(): resizing to " << xmax_size << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + max_size=xmax_size; + cur_size=calculate_size(); + + if (max_size>=0) clear_to_size(enabled ? max_size : 0); +} + +void +DjVuFileCache::enable(bool en) +{ + enabled=en; + set_max_size(max_size); +} + +void +DjVuFileCache::add_file(const GP<DjVuFile> & file) +{ + DEBUG_MSG("DjVuFileCache::add_file(): trying to add a new item\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + // See if the file is already cached + GPosition pos; + for(pos=list;pos;++pos) + if (list[pos]->get_file()==file) break; + + if (pos) list[pos]->refresh(); // Refresh the timestamp + else + { + // Doesn't exist in the list yet + int _max_size=enabled ? max_size : 0; + if (max_size<0) _max_size=max_size; + + int add_size=file->get_memory_usage(); + + if (_max_size>=0 && add_size>_max_size) + { + DEBUG_MSG("but this item is way too large => doing nothing\n"); + return; + } + + if (_max_size>=0) clear_to_size(_max_size-add_size); + + list.append(new Item(file)); + cur_size+=add_size; + file_added(file); + } +} + +void +DjVuFileCache::clear_to_size(int size) +{ + DEBUG_MSG("DjVuFileCache::clear_to_size(): dropping cache size to " << size << "\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + if (size==0) + { + list.empty(); + cur_size=0; + } else + if (list.size()>20) + { + // More than 20 elements in the cache: use qsort to + // sort them before picking up the oldest + GTArray<void *> item_arr(list.size()-1); + GPosition pos; + int i; + for(pos=list, i=0;pos;++pos, i++) + { + GP<Item> item=list[pos]; + item->list_pos=pos; + item_arr[i]=item; + } + + qsort(&item_arr[0], item_arr.size(), sizeof(item_arr[0]), Item::qsort_func); + + for(i=0;i<item_arr.size() && cur_size>(int) size;i++) + { + Item * item=(Item *) item_arr[i]; + cur_size-=item->get_size(); + GP<DjVuFile> file=item->file; + list.del(item->list_pos); + file_cleared(file); + if (cur_size<=0) cur_size=calculate_size(); + } + } else + { + // Less than 20 elements: no reason to presort + while(cur_size>(int) size) + { + if (!list.size()) + { + // Oops. Discrepancy due to an item changed its size + cur_size=0; + break; + } + + // Remove the oldest cache item + GPosition oldest_pos=list; + GPosition pos=list; + for(++pos;pos;++pos) + if (list[pos]->get_time()<list[oldest_pos]->get_time()) + oldest_pos=pos; + cur_size-=list[oldest_pos]->get_size(); + GP<DjVuFile> file=list[oldest_pos]->file; + list.del(oldest_pos); + file_cleared(file); + + // cur_size *may* become negative because items may change their + // size after they've been added to the cache + if (cur_size<=0) cur_size=calculate_size(); + } + } + + DEBUG_MSG("done: current cache size=" << cur_size << "\n"); +} + +int +DjVuFileCache::calculate_size(void) +{ + GCriticalSectionLock lock(&class_lock); + + int size=0; + for(GPosition pos=list;pos;++pos) + size+=list[pos]->get_size(); + return size; +} + +void +DjVuFileCache::del_file(const DjVuFile * file) +{ + DEBUG_MSG("DjVuFileCache::del_file(): Removing an item from cache\n"); + DEBUG_MAKE_INDENT(3); + + GCriticalSectionLock lock(&class_lock); + + for(GPosition pos=list;pos;++pos) + if (list[pos]->get_file()==file) + { + GP<DjVuFile> file=list[pos]->get_file(); + cur_size-=list[pos]->get_size(); + list.del(pos); + file_deleted(file); + break; + } + if (cur_size<0) cur_size=calculate_size(); + DEBUG_MSG("current cache size=" << cur_size << "\n"); +} + +GPList<DjVuFileCache::Item> +DjVuFileCache::get_items(void) +{ + GCriticalSectionLock lock(&class_lock); + + return list; +} + +void +DjVuFileCache::file_added(const GP<DjVuFile> &) {} + +void +DjVuFileCache::file_deleted(const GP<DjVuFile> &) {} + +void +DjVuFileCache::file_cleared(const GP<DjVuFile> &) {} + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h new file mode 100644 index 00000000..9898b8f3 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h @@ -0,0 +1,292 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuFileCache.h,v 1.8 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUFILECACHE_H +#define _DJVUFILECACHE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "DjVuFile.h" + +#ifndef macintosh //MCW can't compile +# ifndef UNDER_CE +# include <sys/types.h> +# include <time.h> +# endif +#else +# include <time.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name DjVuFileCache.h + Files #"DjVuFileCache.h"# and #"DjVuFileCache.cpp"# implement a simple + caching mechanism for keeping a given number of \Ref{DjVuFile} instances + alive. The cache estimates the size of its elements and gets rid of + the oldest ones when necessary. + + See \Ref{DjVuFileCache} for details. + + @memo Simple DjVuFile caching class. + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVuFileCache.h,v 1.8 2003/11/07 22:08:20 leonb Exp $# +*/ + +//@{ + +/** #DjVuFileCache# is a simple list of \Ref{DjVuFile} instances. It keeps + track of the total size of all elements and can get rid of the oldest + one once the total size becomes over some threshold. Its main purpose + is to keep the added \Ref{DjVuFile} instances alive until their size + exceeds some given threshold (set by \Ref{set_maximum_size}() function). + The user is supposed to use \Ref{DjVuPortcaster::name_to_port}() to + find a file corresponding to a given name. The cache provides no + naming services */ +#ifdef UNDER_CE +class DjVuFileCache : public GPEnabled +{ +protected: + DjVuFileCache(const int) {} +public: + static GP<DjVuFileCache> create(const int); + virtual ~DjVuFileCache(void); + void del_file(const DjVuFile *) {} + void add_file(const GP<DjVuFile> &) {} + void clear(void) {} + void set_max_size(int) {} + int get_max_size(void) const {return 0;} + void enable(bool en) {} + bool is_enabled(void) const {return false;} +} ; +#else +class DjVuFileCache : public GPEnabled +{ +protected: + DjVuFileCache(const int max_size=5*2*1024*1024); +public: + /** Constructs the #DjVuFileCache# + @param max_size Maximum allowed size of the cache in bytes. */ + static GP<DjVuFileCache> create(const int max_size=5*2*1024*1024); + + virtual ~DjVuFileCache(void); + + /** Removes file #file# from the cache */ + void del_file(const DjVuFile * file); + + /** Adds the given file to the cache. It it's already there, its + timestamp will be refreshed. */ + void add_file(const GP<DjVuFile> & file); + + /** Clears the cache. All items will be deleted. */ + void clear(void); + /** Sets new maximum size. If the total size of all items in the cache + is greater than #max_size#, the cache will be deleting the oldest + items until the size is OK. */ + void set_max_size(int max_size); + + /** Returns the maximum allowed size of the cache. */ + int get_max_size(void) const; + + /** Enables or disables the cache. See \Ref{is_enabled}() for details + @param en - If {\em en} is TRUE, the cache will be enabled. + Otherwise it will be disabled. + */ + void enable(bool en); + + /** Returns #TRUE# if the cache is enabled, #FALSE# otherwise. + When a cache is disabled, \Ref{add_file}(), and + \Ref{del_file}() do nothing. But the {\em maximum size} is preserved + inside the class so that next time the cache is enabled, it will + be configured the same way. Clearly this "enable/disable" thing is + for convenience only. One could easily simulate this behavior by + setting the {\em maximum size} of the cache to #ZERO#. */ + bool is_enabled(void) const; + +public: + class Item; + + class Item : public GPEnabled + { + public: + virtual ~Item(void); + time_t get_time(void) const; + + GP<DjVuFile> get_file(void) const; + unsigned int get_size(void) const; + + void refresh(void); + + public: + GP<DjVuFile> file; + time_t time; + GPosition list_pos; + static int qsort_func(const void * el1, const void * el2); + + Item(void); + Item(const GP<DjVuFile> & xfile); + }; + +protected: + GCriticalSection class_lock; + + /** This function is called right after the given file has been added + to the cache for management. */ + virtual void file_added(const GP<DjVuFile> & file); + /** This function is called when the given file is no longer + managed by the cache. */ + virtual void file_deleted(const GP<DjVuFile> & file); + /** This function is called when after the cache decides to get rid + of the file. */ + virtual void file_cleared(const GP<DjVuFile> & file); + + GPList<Item> get_items(void); +private: + GPList<Item> list; + bool enabled; + int max_size; + int cur_size; + + int calculate_size(void); + void clear_to_size(int size); +}; + + + +//@} + +inline +DjVuFileCache::Item::Item(void) : time(::time(0)) {} + +inline +DjVuFileCache::Item::Item(const GP<DjVuFile> & xfile) : + file(xfile), time(::time(0)) {} + +inline +DjVuFileCache::Item::~Item(void) {} + +inline GP<DjVuFile> +DjVuFileCache::Item::get_file(void) const +{ + return file; +} + +inline unsigned int +DjVuFileCache::Item::get_size(void) const +{ + return file->get_memory_usage(); +} + +inline time_t +DjVuFileCache::Item::get_time(void) const +{ + return time; +} + +inline void +DjVuFileCache::Item::refresh(void) +{ + time=::time(0); +} + +inline +DjVuFileCache::DjVuFileCache(const int xmax_size) : + enabled(true), max_size(xmax_size), cur_size(0) {} + +inline void +DjVuFileCache::clear(void) +{ + clear_to_size(0); +} + +inline bool +DjVuFileCache::is_enabled(void) const +{ + return enabled; +} + +inline int +DjVuFileCache::get_max_size(void) const +{ + return max_size; +} + +#endif + +inline GP<DjVuFileCache> +DjVuFileCache::create(const int max_size) +{ + return new DjVuFileCache(max_size); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp new file mode 100644 index 00000000..b31b04bf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp @@ -0,0 +1,255 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobal.cpp,v 1.7 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +/** This file impliments the DjVuProgressTask elements. The memory + functions are implimented in a separate file, because only the memory + functions should be compiled with out overloading of the memory functions. + */ + + +#ifdef NEED_DJVU_PROGRESS +#include "DjVuGlobal.h" + + +// ---------------------------------------- + +#include "GOS.h" +#include "GThreads.h" +#include "GException.h" +#include "GContainer.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define INITIAL 500 +#define INTERVAL 250 + +class DjVuProgressTask::Data : public GPEnabled +{ +public: + djvu_progress_callback *callback; + DjVuProgressTask *head; + const char *gtask; + unsigned long lastsigdate; + Data(djvu_progress_callback *_callback): + callback(_callback), head(0), gtask(0), lastsigdate(0) {} +}; + + +static GPMap<void *,DjVuProgressTask::Data> & +get_map(void) +{ + static GPMap<void *,DjVuProgressTask::Data> xmap; + return xmap; +} + +djvu_progress_callback * +DjVuProgressTask::set_callback(djvu_progress_callback *_callback) +{ + djvu_progress_callback *retval=0; + if(_callback) + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + DjVuProgressTask::Data &data=*(map[threadID]); + retval=data.callback; + data.callback=_callback; + data.head=0; + data.gtask=0; + data.lastsigdate=0; + }else + { + map[threadID]=new Data(_callback); + } + }else + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + DjVuProgressTask::Data &data=*(map[threadID]); + retval=data.callback; + data.callback=0; + data.head=0; + data.gtask=0; + data.lastsigdate=0; + map.del(threadID); + } + } + return retval; +} + +DjVuProgressTask::DjVuProgressTask(const char *xtask,int nsteps) + : task(xtask),parent(0), nsteps(nsteps), runtostep(0), gdata(0), data(0) +{ + // gtask=task; + { + GMap<void *,GP<DjVuProgressTask::Data> > &map=get_map(); + void *threadID=GThread::current(); + if(map.contains(threadID)) + { + gdata=new GP<Data>; + Data &d=*(data=((*(GP<Data> *)gdata)=map[threadID])); + if(d.callback) + { + unsigned long curdate = GOS::ticks(); + startdate = curdate; + if (!d.head) + d.lastsigdate = curdate + INITIAL; + parent = d.head; + d.head = this; + } + } + } +} + +DjVuProgressTask::~DjVuProgressTask() +{ + if (data && data->callback) + { + if (data->head != this) + G_THROW( ERR_MSG("DjVuGlobal.not_compatible") ); + data->head = parent; + if (!parent) + { + unsigned long curdate = GOS::ticks(); + if((*(data->callback))(data->gtask?data->gtask:"",curdate-startdate, curdate-startdate)) + { + G_THROW("INTERRUPT"); + } + } + } + delete (GP<Data> *)gdata; +} + +void +DjVuProgressTask::run(int tostep) +{ + if(data) + { + data->gtask=task; + if ((data->callback)&&(tostep>runtostep)) + { + unsigned long curdate = GOS::ticks(); + if (curdate > data->lastsigdate + INTERVAL) + signal(curdate, curdate); + runtostep = tostep; + } + } +} + +void +DjVuProgressTask::signal(unsigned long curdate, unsigned long estdate) +{ + int inprogress = runtostep; + if (inprogress > nsteps) + inprogress = nsteps; + if (inprogress > 0) + { + const unsigned long enddate = startdate+ + (unsigned long)(((float)(estdate-startdate) * (float)nsteps) / (float)inprogress); + if (parent) + { + parent->signal(curdate, enddate); + } + else if (data && data->callback && curdate<enddate) + { + if((*(data->callback))(data->gtask?data->gtask:"",curdate-startdate, enddate-startdate)) + { + G_THROW("INTERRUPT"); + } + data->lastsigdate = curdate; + } + } +} + +// Progress callback +// +djvu_progress_callback * +djvu_set_progress_callback( djvu_progress_callback *callback ) +{ + return DjVuProgressTask::set_callback(callback); +} + +int djvu_supports_progress_callback(void) {return 1;} + +#else + +#ifndef HAS_DJVU_PROGRESS_TYPEDEF +extern "C" +{ + void *djvu_set_progress_callback(void *); + int djvu_supports_progress_callback(void); +} +void *djvu_set_progress_callback(void *) { return 0; } +int djvu_supports_progress_callback(void) {return 0;} +#else +int djvu_supports_progress_callback(void) {return 0;} +djvu_progress_callback * +djvu_set_progress_callback( djvu_progress_callback *) { return 0; } +#endif + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h new file mode 100644 index 00000000..bfdd4dd1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h @@ -0,0 +1,398 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobal.h,v 1.10 2004/08/04 02:36:59 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUGLOBAL_H +#define _DJVUGLOBAL_H +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#if defined(UNDER_CE) +# ifndef __WCEALT_H__ +inline void * operator new(size_t, void * ptr) { return ptr; } +# endif +#elif defined(HAVE_STDINCLUDES) +# include <new> +#else +# include <new.h> +#endif + +#ifdef WIN32 +# ifdef DLL_EXPORT +# define DJVUAPI __declspec(dllexport) +# else +# ifdef LIBDJVU_DLL_IMPORT +# define DJVUAPI __declspec(dllimport) +# endif +# endif +#endif +#ifndef DJVUAPI +# define DJVUAPI +#endif + + +/** @name DjVuGlobal.h + + This file is included by all include files in the DjVu reference library. + + If compilation symbols #NEED_DJVU_MEMORY#, #NEED_DJVU_PROGRESS# + or #NEED_DJVU_NAMES# are defined, this file enables + features which are useful for certain applications of the + DjVu Reference Library. These features are still experimental and + therefore poorly documented. + + @memo + Global definitions. + @version + #$Id: DjVuGlobal.h,v 1.10 2004/08/04 02:36:59 leonb Exp $# + @author + L\'eon Bottou <[email protected]> -- empty file.\\ + Bill Riemers <[email protected]> -- real work. */ +//@{ + + +/** @name DjVu Memory + + This section is enabled when compilation symbol #NEED_DJVU_MEMORY# is + defined. Function #_djvu_memory_callback# can be used to redefine the C++ + memory allocation operators. Some operating systems (e.g. Macintoshes) + require very peculiar memory allocation in shared objects. We redefine + the operators #new# and #delete# as #STATIC_INLINE# because we do not + want to export these redefined versions to other libraries. */ +//@{ +//@} + +#ifdef NEED_DJVU_MEMORY + +# include "DjVu.h" + +// These define the two callbacks needed for C++ +typedef void djvu_delete_callback(void *); +typedef void *djvu_new_callback(size_t); + +// These functions allow users to set the callbacks. +int djvu_memoryObject_callback ( djvu_delete_callback*, djvu_new_callback*); +int djvu_memoryArray_callback ( djvu_delete_callback*, djvu_new_callback*); + +// We need to use this inline function in all modules, but we never want it to +// appear in the symbol table. It seems different compilers need different +// directives to do this... +# ifndef STATIC_INLINE +# ifdef __GNUC__ +# define STATIC_INLINE extern inline +# else /* !__GNUC__ */ +# define STATIC_INLINE static inline +# endif /* __GNUC__ */ +# endif /* STATIC_INLINE */ + +// This clause is used when overriding operator new +// because the standard has slightly changed. +# if defined( __GNUC__ ) && ( __GNUC__*1000 + __GNUC_MINOR__ >= 2091 ) +# ifndef new_throw_spec +# define new_throw_spec throw(std::bad_alloc) +# endif /* new_throw_spec */ +# ifndef delete_throw_spec +# define delete_throw_spec throw() +# endif /* delete_throw_spec */ +# endif /* __GNUC__ ... */ +// Old style +# ifndef new_throw_spec +# define new_throw_spec +# endif /* new_throw_spec */ +# ifndef delete_throw_spec +# define delete_throw_spec +# endif /* delete_throw_spec */ + +# ifdef UNIX +extern djvu_new_callback *_djvu_new_ptr; +extern djvu_new_callback *_djvu_newArray_ptr; +extern djvu_delete_callback *_djvu_delete_ptr; +extern djvu_delete_callback *_djvu_deleteArray_ptr; + +# ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +void *operator new (size_t) new_throw_spec; +void *operator new[] (size_t) new_throw_spec; +void operator delete (void *) delete_throw_spec; +void operator delete[] (void *) delete_throw_spec; + +STATIC_INLINE void * +operator new(size_t sz) new_throw_spec +{ return (*_djvu_new_ptr)(sz); } +STATIC_INLINE void +operator delete(void *addr) delete_throw_spec +{ return (*_djvu_delete_ptr)(addr); } +STATIC_INLINE void * +operator new [] (size_t sz) new_throw_spec +{ return (*_djvu_newArray_ptr)(sz); } +STATIC_INLINE void +operator delete [] (void *addr) delete_throw_spec +{ return (*_djvu_deleteArray_ptr)(addr); } +# endif /* NEED_DJVU_MEMORY_IMPLEMENTATION */ + +# else /* UNIX */ + +# ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +STATIC_INLINE void * +operator new(size_t sz) new_throw_spec +{ return _djvu_new(sz); } +inline_as_macro void +operator delete(void *addr) delete_throw_spec +{ return _djvu_delete(addr); } +inline_as_macro void * +operator new [] (size_t sz) new_throw_spec +{ return _djvu_new(sz); } +inline_as_macro void +operator delete [] (void *addr) delete_throw_spec +{ _djvu_deleteArray(addr); } +# endif /* !NEED_DJVU_MEMORY_IMPLEMENTATION */ + +# endif /* UNIX */ + +#else + +# define _djvu_free(ptr) free((ptr)) +# define _djvu_malloc(siz) malloc((siz)) +# define _djvu_realloc(ptr,siz) realloc((ptr),(siz)) +# define _djvu_calloc(siz,items) calloc((siz),(items)) + +#endif /* NEED_DJVU_MEMORY */ + +/** @name DjVu Progress + + This section is enabled when compilation symbol #NEED_DJVU_PROGRESS# is + defined. This macro setups callback function that may be used to + implement a progress indicator for the encoding routines. The decoding + routines do not need such a facility because it is sufficient to monitor + the calls to function \Ref{ByteStream::read} in class \Ref{ByteStream}. + + {\bf Code tracing macros} --- + Monitoring the progress of such complex algorithms requires significant + code support. This is achieved by inserting {\em code tracing macros} + in strategic regions of the code. + \begin{description} + \item[DJVU_PROGRESS_TASK(name,task,nsteps)] indicates that the current + scope performs a task roughly divided in #nsteps# equal steps, with + the specified #task# string used in the callback. + \item[DJVU_PROGRESS_RUN(name,tostep)] indicates that we are starting + an operation which will take us to step #tostep#. The operation + will be considered finished when #DJVU_PROGRESS_RUN# will be called + again with an argument greater than #tostep#. The execution of + this operation of course can be described by one subtask and so on. + \end{description} + + {\bf Progress callback} --- Before defining the outermost task, you can + store a callback function pointer into the static member variable + #DjVuProgressTask::callback#. This callback function is called + periodically with two unsigned long arguments. The first argument is the + elapsed time. The second argument is the estimated total execution time. + Both times are given in milliseconds. + + {\bf Important Note} --- This monitoring mechanism should not be used by + multithreaded programs. */ +//@{ + +#ifndef HAS_DJVU_PROGRESS_CALLBACKS +# define HAS_DJVU_PROGRESS_CALLBACKS + +# ifdef NEED_DJVU_PROGRESS +# include "DjVu.h" + +extern djvu_progress_callback *_djvu_progress_ptr; + +# define DJVU_PROGRESS_TASK(name,task,nsteps) DjVuProgressTask task_##name(task,nsteps) +# define DJVU_PROGRESS_RUN(name,tostep) { task_##name.run(tostep); } + +class DjVuProgressTask +{ +public: + class Data; + ~DjVuProgressTask(); + DjVuProgressTask(const char *task,int nsteps); + void run(int tostep); + const char *task; + static djvu_progress_callback *set_callback(djvu_progress_callback *ptr=0); +private: + DjVuProgressTask *parent; + int nsteps; + int runtostep; + unsigned long startdate; + // Statics + void *gdata; + Data *data; + // Helpers + void signal(unsigned long curdate, unsigned long estdate); +}; + +# else // ! NEED_DJVU_PROGRESS + +# define DJVU_PROGRESS_TASK(name,task,nsteps) +# define DJVU_PROGRESS_RUN(name,step) + +# endif // ! NEED_DJVU_PROGRESS +#endif // HAS_DJVU_PROGRESS_CALLBACKS +//@} + + +/** @name General functions. + + This section contains functions that replace some of the standard + system calls without any other header file dependancies. + */ + +#ifdef __cplusplus +# define DJVUEXTERNCAPI(x) extern "C" DJVUAPI x; +#else +# define DJVUEXTERNCAPI(x) extern DJVUAPI x +#endif + +/** This replaces fprintf(stderr,...), but with UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintErrorUTF8(const char *fmt, ...)) + +/** This replaces fprintf(stderr,...), but with UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintErrorNative(const char *fmt, ...)) + +/** This replaces printf(...), but requires UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintMessageUTF8(const char *fmt, ...)) + +/** This replaces printf(...), but requires UTF8 encoded strings. */ +DJVUEXTERNCAPI(void DjVuPrintMessageNative(const char *fmt, ...)) + +/** The format (fmt) and arguments define a MessageList to be looked + up in the external messages and printed to stderr. */ +DJVUEXTERNCAPI(void DjVuFormatErrorUTF8(const char *fmt, ...)) + +/** The format (fmt) and arguments define a MessageList to be looked + up in the external messages and printed to stderr. */ +DJVUEXTERNCAPI(void DjVuFormatErrorNative(const char *fmt, ...)) + +/** Prints the translation of message to stderr. */ +DJVUEXTERNCAPI(void DjVuWriteError( const char *message )) + +/** Prints the translation of message to stdout. */ +DJVUEXTERNCAPI(void DjVuWriteMessage( const char *message )) + +/** A C function to perform a message lookup. Arguments are a buffer to + received the translated message, a buffer size (bytes), and a + message_list. The translated result is returned in msg_buffer encoded + in UTF-8. In case of error, msg_buffer is empty + (i.e., msg_buffer[0] == '\0'). +*/ +DJVUEXTERNCAPI(void DjVuMessageLookUpUTF8( + char *msg_buffer, const unsigned int buffer_size, + const char *message )) +DJVUEXTERNCAPI(void DjVuMessageLookUpNative( + char *msg_buffer, const unsigned int buffer_size, + const char *message )) + +/** This function sets the program name used when + searching for language files. +*/ +DJVUEXTERNCAPI(const char *djvu_programname(const char *programname)) + + +/** @name DjVu Names + + This section is enabled when compilation symbol #NEED_DJVU_NAMES# is + defined. This section redefines class names in order to unclutter the + name space of shared objects. This is useful on systems which + automatically export all global symbols when building a shared object. + @args */ +//@{ +//@} + +#ifdef NEED_DJVU_NAMES +/* The contents of this section may be generated by this shell command : + * % egrep -h '^(class|struct) +[A-Z_][A-Za-z0-9_]*' *.h *.cpp |\ + * sed -e 's:[a-z]* *\([A-Za-z_][A-Za-z0-9_]*\).*:#define \1 DJVU_\1:g' |\ + * sort + */ +#endif // NEED_DJVU_NAMES + +//@} + +#if defined(macintosh) +# define EMPTY_LOOP continue +#else +# define EMPTY_LOOP /* nop */ +#endif + +// The ERR_MSG(x) macro is intended to permit automated checking of the +// externalized error message names against the source code. It has no +// effect on the executed program. It should be used to surround each +// message name that will need to be looked up in the external message +// files. In particular, it should use on all strings passed to G_THROW. +#ifndef HAS_CTRL_C_IN_ERR_MSG +# define HAS_CTRL_C_IN_ERR_MSG 1 +#endif +#ifndef ERR_MSG +# if HAS_CTRL_C_IN_ERR_MSG +// This hack allows for the coexistence of internationalized +// and non-internationalized code. All internationalized error +// message names are prefixed with a ctrl-c. Only these will +// be looked for in the message files. Messages that do no +// start with a ctrl-c will remain untranslated. +# define ERR_MSG(x) "\003" x +# else +# define ERR_MSG(x) x +# endif +#endif + +#endif /* _DJVUGLOBAL_H_ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp new file mode 100644 index 00000000..1c684336 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp @@ -0,0 +1,306 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuGlobalMemory.cpp,v 1.6 2003/11/07 22:08:20 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#ifdef NEED_DJVU_MEMORY +#ifndef NEED_DJVU_MEMORY_IMPLEMENTATION +#define NEED_DJVU_MEMORY_IMPLEMENTATION +#endif /* NEED_DJVU_MEMORY_IMPLEMENTATION */ + +#include "DjVuGlobal.h" +#include "GException.h" +#include <stdlib.h> +#include <string.h> +#include "debug.h" + +#ifdef UNIX +djvu_delete_callback * +_djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +djvu_delete_callback * +_djvu_deleteArray_ptr=(djvu_delete_callback *)&(operator delete []); +djvu_new_callback * +_djvu_new_ptr=(djvu_new_callback *)&(operator new); +djvu_new_callback * +_djvu_newArray_ptr=(djvu_new_callback *)&(operator new []); +#endif + +static djvu_delete_callback *_djvu_delete_handler = 0; +static djvu_new_callback *_djvu_new_handler = 0; +static djvu_delete_callback *deleteArray_handler = 0; +static djvu_new_callback *newArray_handler = 0; + +static djvu_free_callback *_djvu_free_handler = 0; +static djvu_realloc_callback *_djvu_realloc_handler = 0; +static djvu_calloc_callback *_djvu_calloc_handler = 0; +static djvu_malloc_callback *_djvu_malloc_handler = 0; + +int +djvu_memoryObject_callback ( + djvu_delete_callback* delete_handler, + djvu_new_callback* new_handler +) { + if(delete_handler && new_handler) + { +#ifdef UNIX + _djvu_new_ptr=&_djvu_new; + _djvu_delete_ptr=&_djvu_delete; +#endif + _djvu_delete_handler=delete_handler; + _djvu_new_handler=new_handler; + return 1; + }else + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&(operator new); + _djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +#endif + _djvu_delete_handler=0; + _djvu_new_handler=0; + return (delete_handler||new_handler)?0:1; + } + return 0; +} + +int +djvu_set_memory_callbacks +( + djvu_free_callback *free_handler, + djvu_realloc_callback *realloc_handler, + djvu_malloc_callback *malloc_handler, + djvu_calloc_callback *calloc_handler +) +{ + if(free_handler && realloc_handler && malloc_handler) + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&_djvu_new; + _djvu_delete_ptr=(djvu_delete_callback *)&_djvu_delete; +#endif + _djvu_new_handler=(djvu_new_callback *)malloc_handler; + _djvu_delete_handler=(djvu_delete_callback *)free_handler; + _djvu_malloc_handler=(djvu_malloc_callback *)malloc_handler; + _djvu_free_handler=(djvu_free_callback *)free_handler; + _djvu_realloc_handler=(djvu_realloc_callback *)realloc_handler; + if(calloc_handler) + { + _djvu_calloc_handler=(djvu_calloc_callback *)&calloc_handler; + }else + { + _djvu_calloc_handler=0; + } + return 1; + }else + { +#ifdef UNIX + _djvu_new_ptr=(djvu_new_callback *)&(operator new); + _djvu_delete_ptr=(djvu_delete_callback *)&(operator delete); +#endif + _djvu_delete_handler=0; + _djvu_new_handler=0; + _djvu_malloc_handler=0; + _djvu_free_handler=0; + _djvu_realloc_handler=0; + _djvu_calloc_handler=0; + return !(_djvu_malloc_handler + ||_djvu_free_handler + ||_djvu_realloc_handler + ||_djvu_calloc_handler); + } +} + +DJVUAPI void * +_djvu_new(size_t siz) +{ + void *ptr; +#ifndef UNIX + if(_djvu_new_handler) + { +#endif + if(!(ptr=(*_djvu_new_handler)(siz?siz:1))) + { + G_THROW( ERR_MSG("DjVuGlobalMemory.exhausted") ); + } +#ifndef UNIX + }else + { + ptr=::operator new(siz?siz:1); + } +#endif + return ptr; +} + +void +_djvu_delete(void *addr) +{ + if(addr) + { + if(_djvu_delete_handler) + { + (*_djvu_delete_handler)(addr); + }else + { + operator delete(addr); + } + } +} + +void * +_djvu_newArray(size_t siz) +{ + void *ptr; +#ifndef UNIX + if(newArray_handler) + { +#endif + if(!(ptr=(*newArray_handler)(siz?siz:1))) + { + G_THROW( ERR_MSG("DjVuGlobalMemory.exhausted") ); + } +#ifndef UNIX + }else + { + ptr=::new unsigned char[siz?siz:1]; + } +#endif + return ptr; +} + +void +_djvu_deleteArray(void *addr) +{ + if(addr) + { + if(deleteArray_handler) + { + (*deleteArray_handler)(addr); + }else + { +#ifdef WIN32 + delete [] (addr) ; +#else + operator delete [] (addr); +#endif + } + } +} + +void * +_djvu_malloc(size_t siz) +{ + DEBUG_MSG("_djvu_malloc: siz="<<siz<<"\n"); + return _djvu_malloc_handler?(*_djvu_malloc_handler)(siz?siz:1):malloc(siz?siz:1); +} + +void * +_djvu_calloc(size_t siz, size_t items) +{ + DEBUG_MSG("_djvu_calloc: siz="<<siz<<" items="<<items<<"\n"); + void *ptr; + if( _djvu_calloc_handler ) + { + ptr = (*_djvu_calloc_handler)(siz?siz:1, items?items:1); + }else if( _djvu_malloc_handler ) + { + if((ptr = (*_djvu_malloc_handler)((siz?siz:1)*(items?items:1)))&&siz&&items) + { + memset(ptr,0,siz*items); + } + }else + { + ptr = calloc(siz?siz:1, items?items:1); + } + return ptr; +} + +void * +_djvu_realloc(void* ptr, size_t siz) +{ + DEBUG_MSG("_djvu_realloc: ptr="<<ptr<<" siz="<<siz<<"\n"); + void *newptr; + if( _djvu_realloc_handler ) + { + newptr = (*_djvu_realloc_handler)(ptr, siz); + }else + { + newptr = realloc(ptr, siz?siz:1); + } + return newptr; +} + +void +_djvu_free(void *ptr) +{ + DEBUG_MSG("_djvu_free: ptr="<<ptr<<"\n"); + if(ptr) + { + if( _djvu_free_handler ) + { + (*_djvu_free_handler)(ptr); + }else + { + free(ptr); + } + } +} + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp new file mode 100644 index 00000000..f384ce97 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp @@ -0,0 +1,1486 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuImage.cpp,v 1.10 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuImage.h" +#include "GScaler.h" +#include "DjVuDocument.h" +#include "DjVuPalette.h" +#include "GContainer.h" +#include "GSmartPointer.h" +#include "JB2Image.h" +#include "IW44Image.h" +#include "DataPool.h" +#include "ByteStream.h" +#include "GMapAreas.h" +#include "DjVuText.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "debug.h" +#include <stdarg.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +//// DJVUIMAGE: CONSTRUCTION + +DjVuImage::DjVuImage(void) +: rotate_count(-1),relayout_sent(false) +{ +} + +void +DjVuImage::connect(const GP<DjVuFile> & xfile) +{ + file=xfile; + DjVuPort::get_portcaster()->add_route(file, this); +} + + + + +//// DJVUIMAGE: DATA COLLECTORS + +GP<DjVuInfo> +DjVuImage::get_info(const GP<DjVuFile> & file) const +{ + if (file->info) + { + if(rotate_count<0) + { + const_cast<DjVuImage *>(this)->init_rotate(*(file->info)); + } + return file->info; + } + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuInfo> info=get_info(list[pos]); + if (info) + { + if(rotate_count<0) + { + const_cast<DjVuImage *>(this)->init_rotate(*(file->info)); + } + return info; + } + } + return 0; +} + +GP<IW44Image> +DjVuImage::get_bg44(const GP<DjVuFile> & file) const +{ + if (file->bg44) + return file->bg44; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<IW44Image> bg44=get_bg44(list[pos]); + if (bg44) + return bg44; + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_bgpm(const GP<DjVuFile> & file) const +{ + if (file->bgpm) + return file->bgpm; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<GPixmap> bgpm=get_bgpm(list[pos]); + if (bgpm) return bgpm; + } + return 0; +} + +GP<JB2Image> +DjVuImage::get_fgjb(const GP<DjVuFile> & file) const +{ + if (file->fgjb) + return file->fgjb; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<JB2Image> fgjb=get_fgjb(list[pos]); + if (fgjb) + return fgjb; + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_fgpm(const GP<DjVuFile> & file) const +{ + if (file->fgpm) + return file->fgpm; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<GPixmap> fgpm=get_fgpm(list[pos]); + if (fgpm) + return fgpm; + } + return 0; +} + +GP<DjVuPalette> +DjVuImage::get_fgbc(const GP<DjVuFile> & file) const +{ + if (file->fgbc) + return file->fgbc; + GPList<DjVuFile> list=file->get_included_files(); + for(GPosition pos=list;pos;++pos) + { + GP<DjVuPalette> fgbc=get_fgbc(list[pos]); + if (fgbc) return fgbc; + } + return 0; +} + +GP<DjVuInfo> +DjVuImage::get_info() const +{ + if (file) + { + return get_info(file); + }else + { + return 0; + } +} + +GP<ByteStream> +DjVuImage::get_anno() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->merge_anno(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<ByteStream> +DjVuImage::get_text() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->get_text(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<ByteStream> +DjVuImage::get_meta() const +{ + GP<ByteStream> out = ByteStream::create(); + ByteStream &mbs = *out; + if (file) + { + file->get_meta(mbs); + } + mbs.seek(0); + if(!mbs.size()) + { + out=0; + } + return out; +} + +GP<IW44Image> +DjVuImage::get_bg44() const +{ + if (file) + return get_bg44(file); + else + return 0; +} + +GP<GPixmap> +DjVuImage::get_bgpm() const +{ + if (file) + return get_bgpm(file); + else + return 0; +} + +GP<JB2Image> +DjVuImage::get_fgjb() const +{ + if (file) + return get_fgjb(file); + else + return 0; +} + +GP<GPixmap> +DjVuImage::get_fgpm() const +{ + if (file) + return get_fgpm(file); + else + return 0; +} + +GP<DjVuPalette> +DjVuImage::get_fgbc() const +{ + if (file) + return get_fgbc(file); + else + return 0; +} + +int +DjVuImage::get_width() const +{ + GP<DjVuInfo> info=get_info(); + return info?((rotate_count&1)?(info->height):(info->width)):0; +} + +int +DjVuImage::get_height() const +{ + GP<DjVuInfo> info=get_info(); + return info?((rotate_count&1)?(info->width):(info->height)):0; +} + +int +DjVuImage::get_real_width() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->width : 0; +} + +int +DjVuImage::get_real_height() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->height : 0; +} + +int +DjVuImage::get_version() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->version : DJVUVERSION; +} + +int +DjVuImage::get_dpi() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->dpi : 300; +} + +int +DjVuImage::get_rounded_dpi() const +{ + return (get_dpi()+5)/10*10; +#if 0 + /* This code used to round the reported dpi to 25, 50, 75, 100, 150, + 300, and 600. Now we just round the dpi to 10ths and return it */ + int dpi=get_dpi(); + if (dpi>700) return dpi; + + const int std_dpi[]={ 25, 50, 75, 100, 150, 300, 600 }; + const int std_dpis=sizeof(std_dpi)/sizeof(std_dpi[0]); + int min_dist=abs(dpi-std_dpi[0]); + int min_idx=0; + for(int i=1;i<std_dpis;i++) + if (abs(std_dpi[i]-dpi)<min_dist) + { + min_dist=abs(std_dpi[i]-dpi); + min_idx=i; + }; + return std_dpi[min_idx]; +#endif +} + +double +DjVuImage::get_gamma() const +{ + GP<DjVuInfo> info=get_info(); + return info ? info->gamma : 2.2; +} + +GUTF8String +DjVuImage::get_mimetype() const +{ + return file ? file->mimetype : GUTF8String(); +} + + +//// DJVUIMAGE: UTILITIES + +GUTF8String +DjVuImage::get_short_description() const +{ + GUTF8String msg = "Empty"; + int width = get_width(); + int height = get_height(); + if (width && height) + if (file && file->file_size>100) + //msg.format("%dx%d in %0.1f Kb", width, height, file->file_size/1024.0); + msg.format( ERR_MSG("DjVuImage.short1") "\t%d\t%d\t%0.1f", width, height, file->file_size/1024.0 ); + else + //msg.format("%dx%d", width, height); + msg.format( ERR_MSG("DjVuImage.short2") "\t%d\t%d", width, height ); + return msg; +} + +GUTF8String +DjVuImage::get_long_description() const +{ + return file?(file->description):GUTF8String(); +} + + +void +DjVuImage::notify_chunk_done(const DjVuPort *, const GUTF8String & name) +{ + if (!relayout_sent && + ( !name.cmp("INFO", 4) || + !name.cmp("PMxx", 2) || + !name.cmp("BMxx", 2) ) ) + { + DjVuPort::get_portcaster()->notify_relayout(this); + relayout_sent=true; + } + else if (!name.cmp("Sxxx", 1) || + !name.cmp("BGxx", 2) || + !name.cmp("FGxx", 2) || + !name.cmp("BMxx", 2) || + !name.cmp("PMxx", 2) ) + DjVuPort::get_portcaster()->notify_redisplay(this); +} + + + + + + +//// DJVUIMAGE: OLD-STYLE DECODING + +DjVuInterface::~DjVuInterface() +{ +} + +class DjVuImageNotifier : public DjVuPort +{ + friend class DjVuImage; + DjVuInterface *notifier; + GP<DataPool> stream_pool; + GURL stream_url; +public: + DjVuImageNotifier(DjVuInterface *notifier); + GP<DataPool> request_data(const DjVuPort *src, const GURL & url); + void notify_chunk_done(const DjVuPort *, const GUTF8String &name); + void notify_redisplay(const class DjVuImage * source); + void notify_relayout(const class DjVuImage * source); +}; + +DjVuImageNotifier::DjVuImageNotifier(DjVuInterface *notifier) + : notifier(notifier) +{ +} + +GP<DataPool> +DjVuImageNotifier::request_data(const DjVuPort *src, const GURL & url) +{ + if (url!=stream_url) + G_THROW( ERR_MSG("DjVuImage.not_decode") ); + return stream_pool; +} + +void +DjVuImageNotifier::notify_redisplay(const class DjVuImage * source) +{ + if (notifier) + notifier->notify_redisplay(); +} + +void +DjVuImageNotifier::notify_relayout(const class DjVuImage * source) +{ + if (notifier) + notifier->notify_relayout(); +} + +void +DjVuImageNotifier::notify_chunk_done(const DjVuPort *, const GUTF8String &name) +{ + if (notifier) + notifier->notify_chunk(name, "" ); +} + +void +DjVuImage::decode(ByteStream & str, DjVuInterface *notifier) +{ + DEBUG_MSG("DjVuImage::decode(): decoding old way...\n"); + DEBUG_MAKE_INDENT(3); + if (file) + G_THROW( ERR_MSG("DjVuImage.bad_call") ); + GP<DjVuImageNotifier> pport = new DjVuImageNotifier(notifier); + pport->stream_url=GURL::UTF8("internal://fake/fake.djvu"); + pport->stream_pool=DataPool::create(); + // Get all the data first + int length; + char buffer[1024]; + while((length=str.read(buffer, 1024))) + pport->stream_pool->add_data(buffer, length); + pport->stream_pool->set_eof(); + GP<DjVuDocument> doc = DjVuDocument::create_wait(pport->stream_url, (DjVuImageNotifier*)pport); + GP<DjVuImage> dimg=doc->get_page(-1, true, (DjVuImageNotifier*)pport); + file=dimg->get_djvu_file(); + if (file->is_decode_stopped()) + G_THROW( DataPool::Stop ); + if (file->is_decode_failed()) + G_THROW( ByteStream::EndOfFile ); // guess + if (!file->is_decode_ok()) + G_THROW( ERR_MSG("DjVuImage.mult_error") ); + DEBUG_MSG("decode DONE\n"); +} + + +//// DJVUIMAGE: CHECKING + +static int +compute_red(int w, int h, int rw, int rh) +{ + for (int red=1; red<16; red++) + if (((w+red-1)/red==rw) && ((h+red-1)/red==rh)) + return red; + return 16; +} + + +int +DjVuImage::is_legal_bilevel() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + // Check info + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check fgjb + if (!fgjb) + return 0; + if (fgjb->get_width()!=width || fgjb->get_height()!=height) + return 0; + // Check that color information is not present. + if (bg44 || bgpm || fgpm) + return 0; + // Ok. + return 1; +} + +int +DjVuImage::is_legal_photo() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + // Check info + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check that extra information is not present. + if (fgjb || fgpm) + return 0; + // Check bg44 + if (bg44 && bg44->get_width()==width && bg44->get_height()==height) + return 1; + // Check bgpm + if (bgpm && (int)bgpm->columns()==width && (int)bgpm->rows()==height) + return 1; + // Ok. + return 0; +} + +int +DjVuImage::is_legal_compound() const +{ + // Components + GP<DjVuInfo> info = get_info(); + GP<JB2Image> fgjb = get_fgjb(); + GP<IW44Image> bg44 = get_bg44(); + GP<GPixmap> bgpm = get_bgpm(); + GP<GPixmap> fgpm = get_fgpm(); + GP<DjVuPalette> fgbc = get_fgbc(); + // Check size + if (! info) + return 0; + int width = info->width; + int height = info->height; + if (! (width>0 && height>0)) + return 0; + // Check fgjb + if (!fgjb) + return 0; + if (fgjb->get_width()!=width || fgjb->get_height()!=height) + return 0; + // Check background + int bgred = 0; + if (bg44) + bgred = compute_red(width, height, bg44->get_width(), bg44->get_height()); + else if (bgpm) + bgred = compute_red(width, height, bgpm->columns(), bgpm->rows()); + if (bgred<1 || bgred>12) + return 0; + // Check foreground colors + int fgred = 0; + if (fgbc) + fgred = 1; + else if (fgpm) + fgred = compute_red(width, height, fgpm->columns(), fgpm->rows()); + if (fgred<1 || fgred>12) + return 0; + // Check that all components are present + if (fgjb && bgred && fgred) + return 1; + // Unrecognized + return 0; +} + + +//// DJVUIMAGE: LOW LEVEL RENDERING + +GP<GBitmap> +DjVuImage::get_bitmap(const GRect &rect, + int subsample, int align) const +{ + // Access image size + int width = get_real_width(); + int height = get_real_height(); + GP<JB2Image> fgjb = get_fgjb(); + if ( width && height && fgjb && + (fgjb->get_width() == width) && + (fgjb->get_height() == height) ) + { + return fgjb->get_bitmap(rect, subsample, align); + } + return 0; +} + +GP<GPixmap> +DjVuImage::get_bg_pixmap(const GRect &rect, + int subsample, double gamma) const +{ + GP<GPixmap> pm = 0; + // Access image size + + GP<DjVuInfo> info = get_info(); + int width = get_real_width(); + int height = get_real_height(); + + + if (width<=0 || height<=0 || !info) return 0; + // Compute gamma_correction + double gamma_correction = 1.0; + if (gamma > 0) + gamma_correction = gamma / info->gamma; + if (gamma_correction < 0.1) + gamma_correction = 0.1; + else if (gamma_correction > 10) + gamma_correction = 10; + + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + // Determine how much bg44 is reduced + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Handle pure downsampling cases + if (subsample == red) + pm = bg44->get_pixmap(1,rect); + else if (subsample == 2*red) + pm = bg44->get_pixmap(2,rect); + else if (subsample == 4*red) + pm = bg44->get_pixmap(4,rect); + else if (subsample == 8*red) + pm = bg44->get_pixmap(8,rect); + // Handle fractional downsampling case + else if (red*4 == subsample*3) + { + GRect nrect = rect; + GRect xrect = rect; + xrect.xmin = (xrect.xmin/3)*4; + xrect.ymin = (xrect.ymin/3)*4; + xrect.xmax = ((xrect.xmax+2)/3)*4; + xrect.ymax = ((xrect.ymax+2)/3)*4; + nrect.translate(-xrect.xmin*3/4, -xrect.ymin*3/4); + if (xrect.xmax > w) + xrect.xmax = w; + if (xrect.ymax > h) + xrect.ymax = h; + GP<GPixmap> ipm = bg44->get_pixmap(1,xrect); + pm = GPixmap::create(); + pm->downsample43(ipm, &nrect); + } + // Handle all other cases with pixmapscaler + else + { + // find suitable power of two + int po2 = 16; + while (po2>1 && subsample<po2*red) + po2 >>= 1; + // setup pixmap scaler + int inw = (w+po2-1)/po2; + int inh = (h+po2-1)/po2; + int outw = (width+subsample-1)/subsample; + int outh = (height+subsample-1)/subsample; + GP<GPixmapScaler> gps=GPixmapScaler::create(inw, inh, outw, outh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red*po2, subsample); + ps.set_vert_ratio(red*po2, subsample); + // run pixmap scaler + GRect xrect; + ps.get_input_rect(rect,xrect); + GP<GPixmap> ipm = bg44->get_pixmap(po2,xrect); + pm = GPixmap::create(); + ps.scale(xrect, *ipm, rect, *pm); + } + // Apply gamma correction + if (pm && gamma_correction!=1.0) + pm->color_correct(gamma_correction); + return pm; + } + + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + // Determine how much bgpm is reduced + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Handle pure downsampling cases + int ratio = subsample/red; + if (subsample==ratio*red && ratio>=1) + { + pm = GPixmap::create(); + if (ratio == 1) + pm->init(*bgpm, rect); + else if (ratio > 1) + pm->downsample(bgpm, ratio, &rect); + } + // Handle all other cases with pixmapscaler + else + { + // setup pixmap scaler + int outw = (width+subsample-1)/subsample; + int outh = (height+subsample-1)/subsample; + GP<GPixmapScaler> gps=GPixmapScaler::create(w, h, outw, outh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red, subsample); + ps.set_vert_ratio(red, subsample); + // run pixmap scaler + pm = GPixmap::create(); + GRect xrect(0,0,w,h); + ps.scale(xrect, *bgpm, rect, *pm); + } + // Apply gamma correction + if (pm && gamma_correction!=1.0) + pm->color_correct(gamma_correction); + return pm; + } + + // FAILURE + return 0; +} + + + +int +DjVuImage::stencil(GPixmap *pm, const GRect &rect, + int subsample, double gamma) const +{ + // Warping and blending. + if (!pm) + return 0; + // Access components + + GP<DjVuInfo> info = get_info(); + int width = get_real_width(); + int height = get_real_height(); + + + if (width<=0 || height<=0 || !info) return 0; + GP<JB2Image> fgjb = get_fgjb(); + GP<GPixmap> fgpm = get_fgpm(); + GP<DjVuPalette> fgbc = get_fgbc(); + + // Compute gamma_correction + double gamma_correction = 1.0; + if (gamma > 0) + gamma_correction = gamma / info->gamma; + if (gamma_correction < 0.1) + gamma_correction = 0.1; + else if (gamma_correction > 10) + gamma_correction = 10; + + // Compute alpha map and relevant JB2Image components + GList<int> components; + GP<GBitmap> bm; + if (fgjb) + { + JB2Image *jimg = fgjb; + if (! (width && height && + jimg->get_width() == width && + jimg->get_height() == height ) ) + return 0; + // Decode bitmap + bm = GBitmap::create(rect.height(), rect.width()); + bm->set_grays(1+subsample*subsample); + int rxmin = rect.xmin * subsample; + int rymin = rect.ymin * subsample; + for (int blitno = 0; blitno < jimg->get_blit_count(); blitno++) + { + const JB2Blit *pblit = jimg->get_blit(blitno); + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + if (pshape.bits && + pblit->left <= rect.xmax * subsample && + pblit->bottom <= rect.ymax * subsample && + pblit->left + (int)pshape.bits->columns() >= rect.xmin * subsample && + pblit->bottom + (int)pshape.bits->rows() >= rect.ymin * subsample ) + { + // Record component list + if (fgbc) components.append(blitno); + // Blit + bm->blit(pshape.bits, + pblit->left - rxmin, pblit->bottom - rymin, + subsample); + } + } + } + + + // TWO LAYER MODEL + if (bm && fgbc) + { + // Perform attenuation from scratch + pm->attenuate(bm, 0, 0); + // Check that fgbc has the correct size + JB2Image *jimg = fgjb; + DjVuPalette *fg = fgbc; + if (jimg->get_blit_count() != fg->colordata.size()) + return 0; + // Copy and color correct palette + int palettesize = fg->size(); + GTArray<GPixel> colors(0,palettesize-1); + for (int i=0; i<palettesize; i++) + fg->index_to_color(i, colors[i]); + GPixmap::color_correct(gamma_correction, colors, palettesize); + // Blit all components (one color at a time) + while (components.size() > 0) + { + GPosition nullpos; + GPosition pos = components; + int lastx = 0; + int colorindex = fg->colordata[components[pos]]; + if (colorindex >= palettesize) + G_THROW( ERR_MSG("DjVuImage.corrupted") ); + // Gather relevant components and relevant rectangle + GList<int> compset; + GRect comprect; + while (pos) + { + int blitno = components[pos]; + const JB2Blit *pblit = jimg->get_blit(blitno); + if (pblit->left < lastx) break; + lastx = pblit->left; + if (fg->colordata[blitno] == colorindex) + { + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + GRect rect(pblit->left, pblit->bottom, + pshape.bits->columns(), pshape.bits->rows()); + comprect.recthull(comprect, rect); + compset.insert_before(nullpos, components, pos); + continue; + } + ++pos; + } + // Round alpha map rectangle + comprect.xmin = comprect.xmin / subsample; + comprect.ymin = comprect.ymin / subsample; + comprect.xmax = (comprect.xmax+subsample-1) / subsample; + comprect.ymax = (comprect.ymax+subsample-1) / subsample; + comprect.intersect(comprect, rect); + // Compute alpha map for that color + bm = 0; + bm = GBitmap::create(comprect.height(), comprect.width()); + bm->set_grays(1+subsample*subsample); + int rxmin = comprect.xmin * subsample; + int rymin = comprect.ymin * subsample; + for (pos=compset; pos; ++pos) + { + int blitno = compset[pos]; + const JB2Blit *pblit = jimg->get_blit(blitno); + const JB2Shape &pshape = jimg->get_shape(pblit->shapeno); + bm->blit(pshape.bits, + pblit->left - rxmin, pblit->bottom - rymin, + subsample); + } + // Blend color into background pixmap + pm->blit(bm, comprect.xmin-rect.xmin, comprect.ymin-rect.ymin, &colors[colorindex]); + } + return 1; + } + + + // THREE LAYER MODEL + if (bm && fgpm) + { + // This follows fig. 4 in Adelson "Layered representations for image + // coding" (1991) http://www-bcs.mit.edu/people/adelson/papers.html. + // The properly warped background is already in PM. The properly warped + // alpha map is already in BM. We just have to warp the foreground and + // perform alpha blending. +#ifdef SIMPLE_THREE_LAYER_RENDERING + int w = fgpm->columns(); + int h = fgpm->rows(); + // Determine foreground reduction + int red = compute_red(width,height, w, h); + if (red<1 || red>12) + return 0; + // Warp foreground pixmap + GPixmapScaler ps(w,h,width/subsample+1,height/subsample+1); + ps.set_horz_ratio(red,subsample); + ps.set_vert_ratio(red,subsample); + GP<GPixmap> nfg = new GPixmap; + GRect provided(0,0,w,h); + ps.scale(provided, *fgpm, rect, *nfg); + // Attenuate background and blit + nfg->color_correct(gamma_correction); + pm->blend(bm, 0, 0, nfg); // blend == attenuate + blit + return 1; +#else + // Things are now a little bit more complex because the convenient + // function GPixmap::stencil() simultaneously upsamples the foreground + // by an integer factor and performs the alpha blending. We have + // to determine how and when this facility can be used. + int w = fgpm->columns(); + int h = fgpm->rows(); + // Determine foreground reduction + int red = compute_red(width,height,w,h); + if (red<1 || red>12) + return 0; + // Try supersampling foreground pixmap by an integer factor + int supersample = ( red>subsample ? red/subsample : 1); + int wantedred = supersample*subsample; + // Try simple foreground upsampling + if (red == wantedred) + { + // Simple foreground upsampling is enough. + pm->stencil(bm, fgpm, supersample, &rect, gamma_correction); + return 1; + } + else + { + // Must pre-warp foreground pixmap + GP<GPixmap> nfg; + int desw = (w*red+wantedred-1)/wantedred; + int desh = (h*red+wantedred-1)/wantedred; + // Cache rescaled fgpm for speed + static const DjVuImage *tagimage = 0; + static const GPixmap *tagfgpm = 0; + static GP<GPixmap> cachednfg = 0; + // Check whether cached fgpm applies. + if ( cachednfg && this==tagimage && fgpm==tagfgpm + && desw==(int)cachednfg->columns() + && desh==(int)cachednfg->rows() ) + { + nfg = cachednfg; + } + else + { + GP<GPixmapScaler> gps=GPixmapScaler::create(w,h,desw,desh); + GPixmapScaler &ps=*gps; + ps.set_horz_ratio(red, wantedred); + ps.set_vert_ratio(red, wantedred); + nfg = GPixmap::create(); + GRect provided(0,0,w,h); + GRect desired(0,0,desw,desh); + ps.scale(provided, *fgpm, desired, *nfg); + } + // Use combined warp+blend function + pm->stencil(bm, nfg, supersample, &rect, gamma_correction); + // Cache + tagimage = this; + tagfgpm = fgpm; + cachednfg = nfg; + return 1; + } +#endif + } + + // FAILURE + return 0; +} + + +GP<GPixmap> +DjVuImage::get_fg_pixmap(const GRect &rect, + int subsample, double gamma) const +{ + // Obtain white background pixmap + GP<GPixmap> pm; + // Access components + const int width = get_real_width(); + const int height = get_real_height(); + if (width && height) + { + pm = GPixmap::create(rect.height(),rect.width(), &GPixel::WHITE); + if (!stencil(pm, rect, subsample, gamma)) + pm=0; + } + return pm; +} + + +GP<GPixmap> +DjVuImage::get_pixmap(const GRect &rect, int subsample, double gamma) const +{ + // Get background + GP<GPixmap> pm = get_bg_pixmap(rect, subsample, gamma); + // Superpose foreground + if (! stencil(pm, rect, subsample, gamma)) + // Avoid ugly progressive display (hack) + if (get_fgjb()) return 0; + // Return + return pm; +} + + +//// DJVUIMAGE: RENDERING (ARBITRARY SCALE) + +typedef GP<GBitmap>(DjVuImage::*BImager)(const GRect &, int, int) const; +typedef GP<GPixmap>(DjVuImage::*PImager)(const GRect &, int, double) const; + +static GP<GBitmap> +do_bitmap(const DjVuImage &dimg, BImager get, + const GRect &inrect, const GRect &inall, int align ) +{ + GRect rect=inrect; + GRect all=inall; +///* rotate code + if( dimg.get_rotate()%4 ) + { + GRectMapper mapper; + mapper.rotate((4-dimg.get_rotate())%4); + mapper.map(rect); + mapper.map(all); + } +///* rotate code ends + + // Sanity + if (! ( all.contains(rect.xmin, rect.ymin) && + all.contains(rect.xmax-1, rect.ymax-1) )) + G_THROW( ERR_MSG("DjVuImage.bad_rect") ); + // Check for integral reduction + int red; + int w = dimg.get_real_width(); + int h = dimg.get_real_height(); + + int rw = all.width(); + int rh = all.height(); + GRect zrect = rect; + zrect.translate(-all.xmin, -all.ymin); + for (red=1; red<=15; red++) + if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red) + { + GP<GBitmap> bm=(dimg.*get)(zrect, red, align); + if(bm) + return bm->rotate((4-dimg.get_rotate())%4); + else + return NULL; + } + // Find best reduction + for (red=15; red>1; red--) + if ( (rw*red < w && rh*red < h) || + (rw*red*3 < w || rh*red*3 < h) ) + break; + // Setup bitmap scaler + if (! (w && h)) return 0; + GP<GBitmapScaler> gbs=GBitmapScaler::create(); + GBitmapScaler &bs=*gbs; + bs.set_input_size( (w+red-1)/red, (h+red-1)/red ); + bs.set_output_size( rw, rh ); + bs.set_horz_ratio( rw*red, w ); + bs.set_vert_ratio( rh*red, h ); + // Scale + GRect srect; + bs.get_input_rect(zrect, srect); + GP<GBitmap> sbm = (dimg.*get)(srect, red, 1); + if (!sbm) return 0; + int border = ((zrect.width() + align - 1) & ~(align - 1)) - zrect.width(); + GP<GBitmap> bm = GBitmap::create(zrect.height(), zrect.width(), border); + bs.scale(srect, *sbm, zrect, *bm); + if( bm ) + return bm->rotate((4-dimg.get_rotate())%4); + else + return NULL; +} + +static GP<GPixmap> +do_pixmap(const DjVuImage &dimg, PImager get, + const GRect &inrect, const GRect &inall, double gamma ) +{ + + GRect rect=inrect; + GRect all=inall; +///* rotate code + if( dimg.get_rotate()%4 ) + { + GRectMapper mapper; + mapper.rotate((4-dimg.get_rotate())%4); + mapper.map(rect); + mapper.map(all); + } +///* rotate code ends + + // Sanity + if (! ( all.contains(rect.xmin, rect.ymin) && + all.contains(rect.xmax-1, rect.ymax-1) )) + G_THROW( ERR_MSG("DjVuImage.bad_rect2") ); + // Check for integral reduction + int red, w=0, h=0, rw=0, rh=0; + w = dimg.get_real_width(); + h = dimg.get_real_height(); + + + rw = all.width(); + rh = all.height(); + GRect zrect = rect; + zrect.translate(-all.xmin, -all.ymin); + for (red=1; red<=15; red++) + if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red) + { + GP<GPixmap> pm = (dimg.*get)(zrect, red, gamma); + if( pm ) + return pm->rotate((4-dimg.get_rotate())%4); + else + return NULL; + } + // These reductions usually go faster (improve!) + static int fastred[] = { 12,6,4,3,2,1 }; + // Find best reduction + for (int i=0; (red=fastred[i])>1; i++) + if ( (rw*red < w && rh*red < h) || + (rw*red*3 < w || rh*red*3 < h) ) + break; + // Setup pixmap scaler + if (w<0 || h<0) return 0; + GP<GPixmapScaler> gps=GPixmapScaler::create(); + GPixmapScaler &ps=*gps; + ps.set_input_size( (w+red-1)/red, (h+red-1)/red ); + ps.set_output_size( rw, rh ); + ps.set_horz_ratio( rw*red, w ); + ps.set_vert_ratio( rh*red, h ); + // Scale + GRect srect; + ps.get_input_rect(zrect, srect); + GP<GPixmap> spm = (dimg.*get)(srect, red, gamma); + if (!spm) return 0; + GP<GPixmap> pm = GPixmap::create(); + ps.scale(srect, *spm, zrect, *pm); + if(pm) + return pm->rotate((4-dimg.get_rotate())%4); + else + return NULL; +} + +GP<GPixmap> +DjVuImage::get_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_pixmap, rect, all, gamma); +} + +GP<GBitmap> +DjVuImage::get_bitmap(const GRect &rect, const GRect &all, int align) const +{ + return do_bitmap(*this, & DjVuImage::get_bitmap, rect, all, align); +} + +GP<GPixmap> +DjVuImage::get_bg_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_bg_pixmap, rect, all, gamma); +} + +GP<GPixmap> +DjVuImage::get_fg_pixmap(const GRect &rect, const GRect &all, double gamma) const +{ + return do_pixmap(*this, & DjVuImage::get_fg_pixmap, rect, all, gamma); +} + +int +DjVuImage::get_rotate() const +{ + return (rotate_count<0)?0:rotate_count; +} + +void +DjVuImage::init_rotate(const DjVuInfo &info) +{ + rotate_count=((360-GRect::findangle(info.orientation))/90)%4; +} + +void DjVuImage::set_rotate(int count) +{ + rotate_count=((count%4)+4)%4; +} + +GP<DjVuAnno> +DjVuImage::get_decoded_anno() +{ + GP<DjVuAnno> djvuanno = DjVuAnno::create(); + GP<ByteStream> bs=get_anno(); + if( bs ) + { + djvuanno->decode(bs); + + const int rotate_count=get_rotate(); + if( rotate_count % 4 ) + { + ///map hyperlinks correctly for rotation + GRect input, output; + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + + GPList<GMapArea> &list=djvuanno->ant->map_areas; + for(GPosition pos=list;pos;++pos) + { + list[pos]->unmap(mapper); + } + } + return djvuanno; + } + else + return NULL; +} + + +void +DjVuImage::map(GRect &rect) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.map(rect); + } +} + +void +DjVuImage::unmap(GRect &rect) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.unmap(rect); + } +} + +void +DjVuImage::map(int &x, int &y) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.map(x, y); + } +} + +void +DjVuImage::unmap(int &x, int &y) const +{ + GRect input, output; + const int rotate_count=get_rotate(); + if(rotate_count%4) + { + input = GRect(0,0,get_width(), get_height()); + output = GRect(0,0, get_real_width(), get_real_height()); + + GRectMapper mapper; + mapper.clear(); + mapper.set_input(input); + mapper.set_output(output); + mapper.rotate((4-rotate_count)%4); + mapper.unmap(x, y); + } +} + +bool +DjVuImage::wait_for_complete_decode(void) +{ + if (file) + { + file->resume_decode(true); + return file->is_decode_ok(); + } + return 0; +} + +// Write out a DjVuXML object tag and map tag. +void +DjVuImage::writeXML(ByteStream &str_out,const GURL &doc_url,const int flags) const +{ + const int height=get_height(); + + static const char *Object="<OBJECT data=\""; + const GURL url(get_djvu_file()->get_url()); + const GUTF8String pagename(url.fname()); + GUTF8String page_param; + if(doc_url.is_valid() && !doc_url.is_empty() && (doc_url != url)) + { + str_out.writestring(Object+doc_url.get_string()); + page_param="<PARAM name=\"PAGE\" value=\""+pagename+"\" />\n"; + }else + { + str_out.writestring(Object+doc_url.get_string()); + } + str_out.writestring("\" type=\""+get_mimetype()+"\" height=\"" + +GUTF8String(height)+"\" width=\""+GUTF8String(get_width()) + +"\" usemap=\""+pagename.toEscaped()+"\" >\n"); + if(!(flags & NOINFO)) + { + const GP<DjVuInfo> info(get_info()); + if(info) + { + info->writeParam(str_out); + } + } + str_out.writestring(page_param); + const GP<DjVuAnno> anno(DjVuAnno::create()); + if(!(flags & NOINFO)||!(flags&NOMAP)) + { + const GP<ByteStream> anno_str(get_anno()); + if(anno_str) + { + anno->decode(anno_str); + } + if(!(flags & NOINFO)) + { + anno->writeParam(str_out); + } + } + if(!(flags & NOTEXT)) + { + const GP<DjVuText> text(DjVuText::create()); + { + const GP<ByteStream> text_str(get_text()); + if(text_str) + { + text->decode(text_str); + } + text->writeText(str_out,height); + } + } + if(!(flags & NOMETA)) + { + const GP<ByteStream> meta_str(get_meta()); + if(meta_str) + { + GP<IFFByteStream> giff=IFFByteStream::create(meta_str); + IFFByteStream &iff=*giff; + GUTF8String chkid; + while( iff.get_chunk(chkid)) + { + GP<ByteStream> gbs(iff.get_bytestream()); + if(chkid == "METa") + { + str_out.copy(*gbs); + //str_out.writestring(gbs->getAsUTF8()); + }else if(chkid == "METz") + { + gbs=BSByteStream::create(gbs); + str_out.copy(*gbs); + //str_out.writestring(gbs->getAsUTF8()); + } + iff.close_chunk(); + } + } + } + str_out.writestring(GUTF8String("</OBJECT>\n")); + if(!(flags & NOMAP)) + { + anno->writeMap(str_out,pagename,height); + } +} + +// Write out a DjVuXML object tag and map tag. +void +DjVuImage::writeXML(ByteStream &str_out) const +{ + writeXML(str_out,GURL()); +} + +// Write out a DjVuXML object tag and map tag. +GUTF8String +DjVuImage::get_XML(const GURL &doc_url,const int flags) const +{ + GP<ByteStream> gbs(ByteStream::create()); + ByteStream &bs=*gbs; + writeXML(bs,doc_url); + bs.seek(0L); + return bs.getAsUTF8(); +} + +// Write out a DjVuXML object tag and map tag. +GUTF8String +DjVuImage::get_XML(void) const +{ + return get_XML(GURL()); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuImage.h b/kviewshell/plugins/djvu/libdjvu/DjVuImage.h new file mode 100644 index 00000000..57b40938 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuImage.h @@ -0,0 +1,449 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuImage.h,v 1.9 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUIMAGE_H +#define _DJVUIMAGE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuImage.h + + Files #"DjVuImage.h"# and #"DjVuImage.cpp"# implement \Ref{DjVuImage} + class produced as a result of decoding of DjVu files. In the previous + version of the library both the rendering {\bf and} decoding have been + handled by \Ref{DjVuImage}. This is no longer true. Now the + \Ref{DjVuDocument} and \Ref{DjVuFile} classes handle decoding of both + single page and multi page documents. + + For compatibility reasons though, we still support old-style decoding + interface through the \Ref{DjVuImage} class for single page documents + {\em only}. As before, the display programs can call the decoding + function from a separate thread. The user interface thread may call + the rendering functions at any time. Rendering will be performed using + the most recent data generated by the decoding thread. This multithreaded + capability enables progressive display of remote images. + + {\bf Creating DjVu images} --- Class \Ref{DjVuImage} does not provide a + direct way to create a DjVu image. The recommended procedure consists of + directly writing the required chunks into an \Ref{IFFByteStream} as + demonstrated in program \Ref{djvumake}. Dealing with too many encoding + issues (such as chunk ordering and encoding quality) would indeed make the + decoder unnecessarily complex. + + {\bf ToDo: Layered structure} --- Class #DjVuImage# currently contains an + unstructured collection of smart pointers to various data structures. + Although it simplifies the rendering routines, this choice does not + reflect the layered structure of DjVu images and does not leave much room + for evolution. We should be able to do better. + + @memo + Decoding DjVu and IW44 images. + @author + L\'eon Bottou <[email protected]> - initial implementation + Andrei Erofeev <[email protected]> - multipage support + @version + #$Id: DjVuImage.h,v 1.9 2005/04/27 16:34:13 leonb Exp $# */ +//@{ + + + +#include "DjVuFile.h" +#include "DjVuAnno.h" +#include "GRect.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/* Obsolete class included for backward compatibility. */ + +class DjVuInterface +{ +public: + virtual ~DjVuInterface(); + virtual void notify_chunk(const char *chkid, const char *msg) = 0; + virtual void notify_relayout(void) = 0; + virtual void notify_redisplay(void) = 0; +}; + + +/** Main DjVu Image data structure. This class defines the internal + representation of a DjVu image. This representation consists of a few + pointers referencing the various components of the DjVu image. These + components are created and populated by the decoding function. The + rendering functions then can use the available components to compute a + pixel representation of the desired segment of the DjVu image. */ + +class DjVuImage : public DjVuPort +{ +protected: + DjVuImage(void); +public: + enum { NOINFO, NOTEXT=1, NOMAP=4, NOMETA=8 }; + // CONSTRUCTION + /** @name Construction. */ + //@{ + /** Creates an empty DjVu image. After the image has been constructed, + it may be connected to an existing \Ref{DjVuFile} or left as is. + + In the former case #DjVuImage# will look for its decoded components + (like #Sjbz# or #BG44#) by decending the hierarchy of \Ref{DjVuFile}s + starting from the one passed to \Ref{connect}(). + + In the latter case you can use \Ref{decode}() function to decode + {\bf single-page} DjVu documents in the old-style way. */ + static GP<DjVuImage> create(void) {return new DjVuImage();} + + /** Connects this #DjVuImage# to the passed \Ref{DjVuFile}. The #DjVuImage# + will use this \Ref{DjVuFile} to retrieve components necessary for + decoding. It will also connect itself to \Ref{DjVuFile} using the + communication mechanism provided by \Ref{DjVuPort} and \Ref{DjVuPortcaster}. + This will allow it to receive and relay messages and requests generated + by the passed \Ref{DjVuFile} and any file included into it. */ + void connect(const GP<DjVuFile> & file); + + /** This combines the above two steps for simplier code operations. */ + static GP<DjVuImage> create(const GP<DjVuFile> &file) + { const GP<DjVuImage> retval=create(); retval->connect(file); return retval; } + + //@} + + // COMPONENTS + /** @name Components. */ + //@{ + /** Returns a pointer to a DjVu information component. + This function returns a null pointer until the decoder + actually processes an #"INFO"# chunk. */ + GP<DjVuInfo> get_info() const; + /** Returns a pointer to the IW44 encoded background component of a DjVu + image. This function returns a null pointer until the decoder actually + processes an #"BG44"# chunk. */ + GP<IW44Image> get_bg44() const; + /** Returns a pointer to the raw background component of a DjVu image. The + background component is used for JPEG encoded backgrounds. This + function returns a null pointer until the decoder actually processes an + #"BGjp"# chunk. */ + GP<GPixmap> get_bgpm() const; + /** Returns a pointer to the mask of the foreground component of a DjVu + image. The mask of the foreground component is always a JB2 image in + this implementation. This function returns a null pointer until the + decoder actually processes an #"Sjbz"# chunk. */ + GP<JB2Image> get_fgjb() const; + /** Returns a pointer to the colors of the foreground component of a DjVu + image. The mask of the foreground component is always a small pixmap in + this implementation. This function returns a null pointer until the + decoder actually processes an #"FG44"# chunk. */ + GP<GPixmap> get_fgpm() const; + /** Returns a pointer to a palette object containing colors for the + foreground components of a DjVu image. These colors are only + pertinent with respect to the JB2Image. */ + GP<DjVuPalette> get_fgbc() const; + /** Returns a pointer to a ByteStream containing all the annotation + chunks collected so far for this image. Individual chunks can be + retrieved using \Ref{IFFByteStream}. Returns NULL if no chunks have been + collected yet. */ + GP<ByteStream> get_anno() const; + /** Returns a pointer to a ByteStream containing all the hidden text. + Returns NULL if no chunks have been collected yet. */ + GP<ByteStream> get_text() const; + /** Returns a pointer to a ByteStream containing all the metadata. + Returns NULL if no chunks have been collected yet. */ + GP<ByteStream> get_meta() const; + //@} + + // NEW STYLE DECODING + /** @name New style decoding. */ + //@{ + /** The decoder is now started when the image is created + by function \Ref{DjVuDocument::get_page} in \Ref{DjVuDocument}. + This function waits until the decoding thread terminates + and returns TRUE if the image has been successfully decoded. */ + bool wait_for_complete_decode(void); + //@} + + // OLD STYLE DECODING + /** @name Old style decoding (backward compatibility). */ + //@{ + /** This function is here for backward compatibility. Now, with the + introduction of multipage DjVu documents, the decoding is handled + by \Ref{DjVuFile} and \Ref{DjVuDocument} classes. For single page + documents though, we still have this wrapper. */ + void decode(ByteStream & str, DjVuInterface *notifier=0); + //@} + + // UTILITIES + /** @name Utilities */ + //@{ + /** Returns the width of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available. This gives rotated width if there is any + rotation of image. If you need real width, use #get_real_width()#.*/ + int get_width() const; + /** Returns the height of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available. This gives rotated height if there is any + rotation of image. If you need real width, use #get_real_height()#.*/ + int get_height() const; + /** Returns the width of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available.*/ + int get_real_width() const; + /** Returns the height of the DjVu image. This function just extracts this + information from the DjVu information component. It returns zero if such + a component is not yet available.*/ + int get_real_height() const; + /** Returns the format version the DjVu data. This function just extracts + this information from the DjVu information component. It returns zero if + such a component is not yet available. This version number should + be compared with the \Ref{DjVu version constants}. */ + int get_version() const; + /** Returns the resolution of the DjVu image. This information is given in + pixels per 2.54 cm. Display programs can use this information to + determine the natural magnification to use for rendering a DjVu + image. */ + int get_dpi() const; + /** Same as \Ref{get_dpi}() but instead of precise value returns the closest + "standard" one: 25, 50, 75, 100, 150, 300, 600. If dpi is greater than + 700, it's returned as is. */ + int get_rounded_dpi() const; + /** Returns the gamma coefficient of the display for which the image was + designed. The rendering functions can use this information in order to + perform color correction for the intended display device. */ + double get_gamma() const; + /** Returns a MIME type string describing the DjVu data. This information + is auto-sensed by the decoder. The MIME type can be #"image/djvu"# or + #"image/iw44"# depending on the data stream. */ + GUTF8String get_mimetype() const; + /** Returns a short string describing the DjVu image. + Example: #"2500x3223 in 23.1 Kb"#. */ + GUTF8String get_short_description() const; + /** Returns a verbose description of the DjVu image. This description lists + all the chunks with their size and a brief comment, as shown in the + following example. + \begin{verbatim} + DJVU Image (2325x3156) version 17: + 0.0 Kb 'INFO' Page information. + 17.3 Kb 'Sjbz' JB2 foreground mask (2325x3156) + 2.5 Kb 'BG44' IW44 background (775x1052) + 1.0 Kb 'FG44' IW44 foreground colors (194x263) + 3.0 Kb 'BG44' IW44 background (part 2). + 0.9 Kb 'BG44' IW44 background (part 3). + 7.1 Kb 'BG44' IW44 background (part 4). + Compression ratio: 676 (31.8 Kb) + \end{verbatim} */ + GUTF8String get_long_description() const; + /** Returns pointer to \Ref{DjVuFile} which contains this image in + compressed form. */ + GP<DjVuFile> get_djvu_file(void) const; + /// Write out a DjVuXML object tag and map tag. + void writeXML(ByteStream &str_out,const GURL &doc_url, const int flags=0) const; + /// Write out a DjVuXML object tag and map tag. + void writeXML(ByteStream &str_out) const; + /// Get a DjVuXML object tag and map tag. + GUTF8String get_XML(const GURL &doc_url, const int flags=0) const; + /// Get a DjVuXML object tag and map tag. + GUTF8String get_XML(void) const; + //@} + + // CHECKING + /** @name Checking for legal DjVu files. */ + //@{ + /** This function returns true if this object contains a well formed {\em + Photo DjVu Image}. Calling function #get_pixmap# on a well formed photo + image should always return a non zero value. Note that function + #get_pixmap# works as soon as sufficient information is present, + regardless of the fact that the image follows the rules or not. */ + int is_legal_photo() const; + /** This function returns true if this object contains a well formed {\em + Bilevel DjVu Image}. Calling function #get_bitmap# on a well formed + bilevel image should always return a non zero value. Note that function + #get_bitmap# works as soon as a foreground mask component is present, + regardless of the fact that the image follows the rules or not. */ + int is_legal_bilevel() const; + /** This function returns true if this object contains a well formed {\em + Compound DjVu Image}. Calling function #get_bitmap# or #get_pixmap# on + a well formed compound DjVu image should always return a non zero value. + Note that functions #get_bitmap# or #get_pixmap# works as soon as + sufficient information is present, regardless of the fact that the image + follows the rules or not. */ + int is_legal_compound() const; + //@} + + // RENDERING + /** @name Rendering. + All these functions take two rectangles as argument. Conceptually, + these function first render the whole image into a rectangular area + defined by rectangle #all#. The relation between this rectangle and the + image size define the appropriate scaling. The rendering function then + extract the subrectangle #rect# and return the corresponding pixels as a + #GPixmap# or #GBitmap# object. The #all# and #rect# should take the any + rotation in to effect, The actual implementation performs these + two operation simultaneously for obvious efficiency reasons. The best + rendering speed is achieved by making sure that the size of rectangle + #all# and the size of the DjVu image are related by an integer ratio. */ + //@{ + /** Renders the image and returns a color pixel image. Rectangles #rect# + and #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. + This function returns a null pointer if there is not enough information + in the DjVu image to properly render the desired image. */ + GP<GPixmap> get_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + /** Renders the mask of the foreground layer of the DjVu image. This + functions is a wrapper for \Ref{JB2Image::get_bitmap}. Argument #align# + specified the alignment of the rows of the returned images. Setting + #align# to #4#, for instance, will adjust the bitmap border in order to + make sure that each row of the returned image starts on a word (four + byte) boundary. This function returns a null pointer if there is not + enough information in the DjVu image to properly render the desired + image. */ + GP<GBitmap> get_bitmap(const GRect &rect, const GRect &all, int align = 1) const; + /** Renders the background layer of the DjVu image. Rectangles #rect# and + #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. This + function returns a null pointer if there is not enough information in + the DjVu image to properly render the desired image. */ + GP<GPixmap> get_bg_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + /** Renders the foreground layer of the DjVu image. Rectangles #rect# and + #all# are used as explained above. Color correction is performed + according to argument #gamma#, which represents the gamma coefficient of + the display device on which the pixmap will be rendered. The default + value, zero, means that no color correction should be performed. This + function returns a null pointer if there is not enough information in + the DjVu image to properly render the desired image. */ + GP<GPixmap> get_fg_pixmap(const GRect &rect, const GRect &all, double gamma=0) const; + + + /** set the rotation count(angle) in counter clock wise for the image + values (0,1,2,3) correspond to (0,90,180,270) degree rotation*/ + void set_rotate(int count=0); + /** returns the rotation count*/ + int get_rotate() const; + /** returns decoded annotations in DjVuAnno object in which all hyperlinks + and hilighted areas are rotated as per rotation setting*/ + GP<DjVuAnno> get_decoded_anno(); + /** maps the given #rect# from rotated co-ordinates to unrotated document + co-ordinates*/ + void map(GRect &rect) const; + /** unmaps the given #rect# from unrotated document co-ordinates to rotated + co-ordinates*/ + void unmap(GRect &rect) const; + /** maps the given #x#, #y# from rotated co-ordinates to unrotated document + co-ordinates*/ + void map(int &x, int &y) const; + /** unmaps the given #x#, #y# from unrotated document co-ordinates to rotated + co-ordinates*/ + void unmap(int &x, int &y) const; + + + + //@} + + // Inherited from DjVuPort. + virtual void notify_chunk_done(const DjVuPort *, const GUTF8String &name); + + // SUPERSEDED + GP<GPixmap> get_pixmap(const GRect &rect, int subs=1, double gamma=0) const; + GP<GBitmap> get_bitmap(const GRect &rect, int subs=1, int align = 1) const; + GP<GPixmap> get_bg_pixmap(const GRect &rect, int subs=1, double gamma=0) const; + GP<GPixmap> get_fg_pixmap(const GRect &rect, int subs=1, double gamma=0) const; +private: + GP<DjVuFile> file; + int rotate_count; + bool relayout_sent; + + // HELPERS + int stencil(GPixmap *pm, const GRect &rect, int subs, double gcorr) const; + GP<DjVuInfo> get_info(const GP<DjVuFile> & file) const; + GP<IW44Image> get_bg44(const GP<DjVuFile> & file) const; + GP<GPixmap> get_bgpm(const GP<DjVuFile> & file) const; + GP<JB2Image> get_fgjb(const GP<DjVuFile> & file) const; + GP<GPixmap> get_fgpm(const GP<DjVuFile> & file) const; + GP<DjVuPalette> get_fgbc(const GP<DjVuFile> & file) const; + void init_rotate(const DjVuInfo &info); +}; + + +inline GP<DjVuFile> +DjVuImage::get_djvu_file(void) const +{ + return file; +} + +//@} + + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp new file mode 100644 index 00000000..13ae6480 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp @@ -0,0 +1,205 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuInfo.cpp,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuInfo.h" +#include "GException.h" +#include "ByteStream.h" +#include "GString.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// ---------------------------------------- +// CLASS DJVUINFO + + +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + + +DjVuInfo::DjVuInfo() + : width(0), height(0), +#ifdef DJVUVERSION_FOR_OUTPUT + version(DJVUVERSION_FOR_OUTPUT), +#else + version(DJVUVERSION), +#endif + dpi(300), gamma(2.2), compressable(false), orientation(GRect::BULRNR) +{ +} + +void +DjVuInfo::decode(ByteStream &bs) +{ + // Set to default values + width = 0; + height = 0; + version = DJVUVERSION; + dpi = 300; + gamma = 2.2; + compressable=false; + orientation=GRect::BULRNR; + // Read data + unsigned char buffer[10]; + int size = bs.readall((void*)buffer, sizeof(buffer)); + if (size == 0) + G_THROW( ByteStream::EndOfFile ); + if (size < 5) + G_THROW( ERR_MSG("DjVuInfo.corrupt_file") ); + // Analyze data with backward compatibility in mind! + if (size>=2) + width = (buffer[0]<<8) + buffer[1]; + if (size>=4) + height = (buffer[2]<<8) + buffer[3]; + if (size>=5) + version = buffer[4]; + if (size>=6 && buffer[5]!=0xff) + version = (buffer[5]<<8) + buffer[4]; + if (size>=8 && buffer[7]!=0xff) + dpi = (buffer[7]<<8) + buffer[6]; + if (size>=9) + gamma = 0.1 * buffer[8]; + int flags=0; + if (size>=10) + flags = buffer[9]; + // Fixup + if (gamma<0.3) + gamma=0.3; + if (gamma>5.0) + gamma=5.0; + if (dpi < 25 || dpi > 6000) + dpi = 300; + if(flags&COMPRESSABLE_FLAG) + compressable=true; + if(version>=DJVUVERSION_ORIENTATION) + { + orientation=(GRect::Orientations)(flags&((int)GRect::BOTTOM_UP|(int)GRect::MIRROR|(int)GRect::ROTATE90_CW)); + } +} + +void +DjVuInfo::encode(ByteStream &bs) +{ + bs.write16(width); + bs.write16(height); + bs.write8(version & 0xff); + bs.write8(version >> 8); + bs.write8(dpi & 0xff); + bs.write8(dpi >> 8); + bs.write8((int)(10.0*gamma+0.5) ); + unsigned char flags=orientation; + if(compressable) + { + flags|=COMPRESSABLE_FLAG; + } + bs.write8(flags); +} + +unsigned int +DjVuInfo::get_memory_usage() const +{ + return sizeof(DjVuInfo); +} + +GUTF8String +DjVuInfo::get_paramtags(void) const +{ + const int angle=GRect::findangle(orientation); + GUTF8String retval; + if(angle) + { + retval+="<PARAM name=\"ROTATE\" value=\""+GUTF8String(angle)+"\" />\n"; + } + if(orientation == GRect::rotate(angle,GRect::TDLRNR)) + { + retval+="<PARAM name=\"VFLIP\" value=\"true\" />\n"; + } + if(dpi) + { + retval+="<PARAM name=\"DPI\" value=\""+GUTF8String(dpi)+"\" />\n"; + } + if(gamma) + { + retval+="<PARAM name=\"GAMMA\" value=\""+GUTF8String(gamma)+"\" />\n"; + } + return retval; +} + +void +DjVuInfo::writeParam(ByteStream &str_out) const +{ + str_out.writestring(get_paramtags()); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h new file mode 100644 index 00000000..67a83be9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuInfo.h @@ -0,0 +1,193 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuInfo.h,v 1.12 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUINFO_H +#define _DJVUINFO_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name DjVuInfo.h + Each instance of class #DjVuInfo# represents the information + contained in the information chunk of a DjVu file. This #"INFO"# + chunk is always the first chunk of a DjVu file. + @memo + DjVu information chunk. + @author + L\'eon Bottou <[email protected]> + @version + #$Id: DjVuInfo.h,v 1.12 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + + +#include "GSmartPointer.h" +#include "GRect.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVu version constants + @memo DjVu file format version. */ +//@{ +/** Current DjVu format version. The value of this macro represents the + version of the DjVu file format implemented by this release of the DjVu + Reference Library. */ +#define DJVUVERSION 25 +/** DjVu format version. This is the value used in files produced + with DjVuLibre. This is smaller than DJVUVERSION because version + number inflation causes problems with older software. + */ +#define DJVUVERSION_FOR_OUTPUT 24 +/** This is the version which introduced orientations. */ +#define DJVUVERSION_ORIENTATION 22 +/** Oldest DjVu format version supported by this library. This release of the + library cannot completely decode DjVu files whose version field is less + than or equal to this number. */ +#define DJVUVERSION_TOO_OLD 15 +/** Newest DjVu format partially supported by this library. This release of + the library will attempt to decode files whose version field is smaller + than this macro. If the version field is greater than or equal to this + number, the decoder will just throw a \Ref{GException}. */ +#define DJVUVERSION_TOO_NEW 50 +//@} + + +class GUTF8String; + +/** Information component. + Each instance of class #DjVuInfo# represents the information + contained in the information chunk of a DjVu file. This #"INFO"# + chunk is always the first chunk of a DjVu file. + */ + +class DjVuInfo : public GPEnabled +{ +protected: + DjVuInfo(void); +public: + /** Creates an empty DjVuInfo object. + The #width# and #height# fields are set to zero. + All other fields are initialized with suitable default values. */ + static GP<DjVuInfo> create(void) {return new DjVuInfo();} + + /** Decodes the DjVu #"INFO"# chunk. This function reads binary data from + ByteStream #bs# and populates the fields of this DjVuInfo object. It is + normally called after detecting an #"INFO"# chunk header with function + \Ref{IFFByteStream::get_chunk}. */ + void decode(ByteStream &bs); + /** Encodes the DjVu #"INFO"# chunk. This function writes the fields of this + DjVuInfo object into ByteStream #bs#. It is normally called after + creating an #"INFO"# chunk header with function + \Ref{IFFByteStream::put_chunk}. */ + void encode(ByteStream &bs); + /** Returns the number of bytes used by this object. */ + unsigned int get_memory_usage() const; + /** Width of the DjVu image (in pixels). */ + int width; + /** Height of the DjVu image (in pixels). */ + int height; + /** DjVu file version number. This number characterizes the file format + version used by the encoder to generate this DjVu image. A decoder + should compare this version number with the constants described in + section "\Ref{DjVu version constants}". */ + int version; + /** Resolution of the DjVu image. The resolution is given in ``pixels per + 2.54 centimeters'' (this unit is sometimes called ``pixels per + inch''). Display programs can use this information to determine the + natural magnification to use for rendering a DjVu image. */ + int dpi; + /** Gamma coefficient of the display for which the image was designed. The + rendering functions can use this information in order to perform color + correction for the intended display device. */ + double gamma; + /** The following boolian values are stored in the last character of the + info structure. Unused bits are reserved for possible future extensions + and backwards compatability. */ + bool compressable; + enum {COMPRESSABLE_FLAG=0x80,RESERVED_FLAGS1=0x7f}; + + /** We also store the current image orientation as three bits. */ + GRect::Orientations orientation; + + /// Obtain the flags for the default specifications. + GUTF8String get_paramtags(void) const; + /// Obtain the flags for the default specifications. + void writeParam(ByteStream &out_str) const; +}; + + +//@} + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp new file mode 100644 index 00000000..e92b7570 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp @@ -0,0 +1,647 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessage.cpp,v 1.16 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// All these XML messages are Lizardtech innovations. + +#include "DjVuMessage.h" +#include "GOS.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GURL.h" +#include "debug.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#ifdef WIN32 +# include <tchar.h> +# include <atlbase.h> +# include <windows.h> +# include <winreg.h> +#endif +#ifdef UNIX +# include <unistd.h> +# include <pwd.h> +# include <sys/types.h> +#endif +#include <locale.h> +#ifndef LC_MESSAGES +# define LC_MESSAGES LC_ALL +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +GUTF8String & +DjVuMessage::programname(void) +{ + static GUTF8String xprogramname; + use_language(); + return xprogramname; +} + +static const char namestring[]="name"; +static const char srcstring[]="src"; + +static const char *failed_to_parse_XML=ERR_MSG("DjVuMessage.failed_to_parse_XML"); +static const char bodystring[]="BODY"; +static const char languagestring[]="LANGUAGE"; +static const char headstring[]="HEAD"; +static const char includestring[]="INCLUDE"; +static const char messagestring[]="MESSAGE"; +static const char localestring[]="locale"; + + +// directory names for searching messages +static const char opensourcedir[]="osi"; +#ifdef AUTOCONF +static const char DjVuDataDir[] = DIR_DATADIR "/djvu"; +static const char ModuleDjVuDir[] ="share/djvu"; +#else /* !AUTOCONF */ +static const char ModuleDjVuDir[] ="profiles"; +#endif /* !AUTOCONF */ +static const char LocalDjVuDir[] =".DjVu"; // relative to ${HOME} +#ifdef LT_DEFAULT_PREFIX +static const char DjVuPrefixDir[] = LT_DEFAULT_PREFIX "/profiles"; +#endif +#ifndef NDEBUG +static const char DebugModuleDjVuDir[] ="../TOPDIR/SRCDIR/profiles"; +#endif +#ifdef WIN32 +static const char RootDjVuDir[] ="C:/Program Files/LizardTech/Profiles"; +static const TCHAR registrypath[]= TEXT("Software\\LizardTech\\DjVu\\Profile Path"); +#else +static const char RootDjVuDir[] ="/etc/DjVu/"; // global last resort +#endif + +static const char DjVuEnv[] = "DJVU_CONFIG_DIR"; + +// The name of the message file +static const char MessageFile[]="messages.xml"; +static const char LanguageFile[]="languages.xml"; + +#ifdef WIN32 +static GURL +RegOpenReadConfig ( HKEY hParentKey ) +{ + GURL retval; + // To do: This needs to be shared with SetProfile.cpp + LPCTSTR path = registrypath; + + HKEY hKey = 0; + // MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,argv[1],strlen(argv[1])+1,wszSrcFile,sizeof(wszSrcFile)); + if (RegOpenKeyEx(hParentKey, path, 0, + KEY_READ, &hKey) == ERROR_SUCCESS ) + { + TCHAR path[1024]; + // Success + TCHAR *szPathValue = path; + LPCTSTR lpszEntry = (LPCTSTR &)TEXT(""); + DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1; + DWORD dwType; + + LONG lResult = RegQueryValueEx(hKey, lpszEntry, NULL, + &dwType, (LPBYTE) szPathValue, &dwCount); + + RegCloseKey(hKey); + + if ((lResult == ERROR_SUCCESS)) + { + szPathValue[dwCount] = 0; + USES_CONVERSION; + retval=GURL::Filename::Native(T2CA(path)); + } + } +// if (hKey) RegCloseKey(hKey); + return retval; +} + +static GURL +GetModulePath( void ) +{ + const GUTF8String cwd(GOS::cwd()); + TCHAR path[1024]; + DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1; + GetModuleFileName(0, path, dwCount); + USES_CONVERSION; + GURL retval=GURL::Filename::Native(T2CA(path)).base(); + GOS::cwd(cwd); + return retval; +} +#elif defined(UNIX) + +static GList<GURL> +parsePATH(void) +{ + GList<GURL> retval; + const char *path=getenv("PATH"); + if(path) + { + GNativeString p(path); + int from=0; + for(int to;(to=p.search(':',from))>0;from=to+1) + { + if(to > from) + { + retval.append(GURL::Filename::Native(p.substr(from,to-from))); + } + } + if((from+1)<(int)p.length()) + { + retval.append(GURL::Filename::Native(p.substr(from,-1))); + } + } + return retval; +} + +static GURL +GetModulePath( void ) +{ + GURL retval; + GUTF8String &xprogramname=DjVuMessage::programname(); + if(xprogramname.length()) + { + if(xprogramname[1]=='/' + ||!xprogramname.cmp("../",3) + ||!xprogramname.cmp("./",2)) + { + retval=GURL::Filename::UTF8(xprogramname); + } + if(retval.is_empty() || !retval.is_file()) + { + GList<GURL> paths(parsePATH()); + GMap<GUTF8String,void *> pathMAP; + for(GPosition pos=paths;pos;++pos) + { + retval=GURL::UTF8(xprogramname,paths[pos]); + const GUTF8String path(retval.get_string()); + if(!pathMAP.contains(path)) + { + if(retval.is_file()) + break; + pathMAP[path]=0; + } + } + } + if (! retval.is_empty() ) + retval = retval.follow_symlinks(); + if (! retval.is_empty() ) + retval = retval.base(); + } + return retval; +} +#endif + +static void +appendPath(const GURL &url, + GMap<GUTF8String,void *> &map, + GList<GURL> &list) +{ + if( !url.is_empty() + && !map.contains(url.get_string()) && url.is_dir() ) + { + map[url.get_string()]=0; + list.append(url); + } +} + +GList<GURL> +DjVuMessage::GetProfilePaths(void) +{ + static bool first=true; + static GList<GURL> realpaths; + if(first) + { + first=false; + GMap<GUTF8String,void *> pathsmap; + GList<GURL> paths; + GURL path; + const GUTF8String envp(GOS::getenv(DjVuEnv)); + if(envp.length()) + appendPath(GURL::Filename::UTF8(envp),pathsmap,paths); +#if defined(WIN32) || defined(UNIX) + GURL mpath(GetModulePath()); + if(!mpath.is_empty() && mpath.is_dir()) + { +#if defined(UNIX) && !defined(AUTOCONF) && !defined(NDEBUG) + appendPath(GURL::UTF8(DebugModuleDjVuDir,mpath),pathsmap,paths); +#endif + appendPath(mpath,pathsmap,paths); + mpath=mpath.base(); + appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths); + mpath=mpath.base(); + appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths); + } +#endif +#if defined(AUTOCONF) + GURL dpath = GURL::Filename::UTF8(DjVuDataDir); + appendPath(dpath,pathsmap,paths); +#endif +#ifdef WIN32 + appendPath(RegOpenReadConfig(HKEY_CURRENT_USER),pathsmap,paths); + appendPath(RegOpenReadConfig(HKEY_LOCAL_MACHINE),pathsmap,paths); +#else + GUTF8String home=GOS::getenv("HOME"); +# if HAVE_GETPWUID + if (! home.length()) { + struct passwd *pw=0; + if ((pw = getpwuid(getuid()))) + home=GNativeString(pw->pw_dir); + } +# endif + if (home.length()) { + GURL hpath = GURL::UTF8(LocalDjVuDir,GURL::Filename::UTF8(home)); + appendPath(hpath,pathsmap,paths); + } +#endif +#ifdef LT_DEFAULT_PREFIX + appendPath(GURL::Filename::UTF8(DjVuPrefixDir),pathsmap,paths); +#endif + appendPath(GURL::Filename::UTF8(RootDjVuDir),pathsmap,paths); + pathsmap.empty(); + + GPosition pos; + GList< GMap<GUTF8String,GP<lt_XMLTags> > > localemaps; + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(LanguageFile,paths[pos]); + if(path.is_file()) + { + const GP<lt_XMLTags> xml(lt_XMLTags::create(ByteStream::create(path,"rb"))); + const GPList<lt_XMLTags> Body(xml->get_Tags(bodystring)); + GPosition pos=Body; + if(!pos || (pos != Body.lastpos())) + { + G_THROW( ERR_MSG("XMLAnno.extra_body") ); + } + const GP<lt_XMLTags> GBody(Body[pos]); + if(!GBody) + { + G_THROW( ERR_MSG("XMLAnno.no_body") ); + } + GMap<GUTF8String,GP<lt_XMLTags> > localemap; + lt_XMLTags::get_Maps(languagestring,localestring,Body,localemap); + localemaps.append(localemap); + } + } + GList<GURL> localepaths; + GList<GURL> osilocalepaths; + + // Need to do it the right way! + GUTF8String defaultlocale = getenv("LANGUAGE"); + if (! defaultlocale) + { + const GUTF8String oldlocale(setlocale(LC_MESSAGES,0)); + defaultlocale = setlocale(LC_MESSAGES,""); + setlocale(LC_MESSAGES,(const char *)oldlocale); + } + // Unfathomable search. + for(int loop=0; loop<2; loop++) + { + static const char sepchars[]=" _.@"; + const char *p=sepchars+sizeof(sepchars)-1; + do + { + int sepcharpos=p[0]?defaultlocale.search(p[0]):defaultlocale.length(); + if(sepcharpos > 0) + { + const GUTF8String sublocale(defaultlocale,sepcharpos); + const GUTF8String downcasesublocale("downcase^"+sublocale.downcase()); + for(pos=localemaps;pos;++pos) + { + const GMap<GUTF8String,GP<lt_XMLTags> > &localemap=localemaps[pos]; + GPosition pos=localemap.contains(sublocale); + if(!pos) + pos=localemap.contains(downcasesublocale); + if(pos) + { + const GMap<GUTF8String,GUTF8String>&args + = localemap[pos]->get_args(); + pos = args.contains(srcstring); + if (pos) + { + const GUTF8String src(args[pos]); + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(src,paths[pos]); + if(path.is_dir()) + localepaths.append(path); + path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+src,paths[pos]); + if(path.is_dir()) + osilocalepaths.append(path); + } + } + // We don't need to check anymore language files. + p=sepchars; + break; + } + } + if(!pos) + { + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(sublocale,paths[pos]); + if(path.is_dir()) + { + localepaths.append(path); + } + path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+sublocale,paths[pos]); + if(path.is_dir()) + { + osilocalepaths.append(path); + } + } + } + } + } while(p-- != sepchars); + if((GPosition) localepaths) + break; + defaultlocale="C"; + } + for(pos=localepaths;pos;++pos) + appendPath(localepaths[pos],pathsmap,realpaths); + for(pos=paths;pos;++pos) + appendPath(paths[pos],pathsmap,realpaths); + for(pos=osilocalepaths;pos;++pos) + appendPath(osilocalepaths[pos],pathsmap,realpaths); + for(pos=paths;pos;++pos) + { + path=GURL::UTF8(opensourcedir,paths[pos]); + appendPath(path,pathsmap,realpaths); + } + } + return realpaths; +} + +static GUTF8String +getbodies( + GList<GURL> &paths, + const GUTF8String &MessageFileName, + GPList<lt_XMLTags> &body, + GMap<GUTF8String, void *> & map ) +{ + GUTF8String errors; + bool isdone=false; + GPosition firstpathpos=paths; + for(GPosition pathpos=firstpathpos;!isdone && pathpos;++pathpos) + { + const GURL::UTF8 url(MessageFileName,paths[pathpos]); + if(url.is_file()) + { + map[MessageFileName]=0; + GP<lt_XMLTags> gtags; + { + GP<ByteStream> bs=ByteStream::create(url,"rb"); + G_TRY + { + gtags=lt_XMLTags::create(bs); + } + G_CATCH(ex) + { + GUTF8String mesg(failed_to_parse_XML+("\t"+url.get_string())); + if(errors.length()) + { + errors+="\n"+mesg; + }else + { + errors=mesg; + } + errors+="\n"+GUTF8String(ex.get_cause()); + } + G_ENDCATCH; + } + if(gtags) + { + lt_XMLTags &tags=*gtags; + GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring); + if(! Bodies.isempty()) + { + isdone=true; + for(GPosition pos=Bodies;pos;++pos) + { + body.append(Bodies[pos]); + } + } + GPList<lt_XMLTags> Head=tags.get_Tags(headstring); + if(! Head.isempty()) + { + isdone=true; + GMap<GUTF8String, GP<lt_XMLTags> > includes; + lt_XMLTags::get_Maps(includestring,namestring,Head,includes); + for(GPosition pos=includes;pos;++pos) + { + const GUTF8String file=includes.key(pos); + if(! map.contains(file)) + { + GList<GURL> xpaths; + xpaths.append(url.base()); + const GUTF8String err2(getbodies(xpaths,file,body,map)); + if(err2.length()) + { + if(errors.length()) + { + errors+="\n"+err2; + }else + { + errors=err2; + } + } + } + } + } + } + } + } + return errors; +} + +static GUTF8String +parse(GMap<GUTF8String,GP<lt_XMLTags> > &retval) +{ + GUTF8String errors; + GPList<lt_XMLTags> body; + { + GList<GURL> paths=DjVuMessage::GetProfilePaths(); + GMap<GUTF8String, void *> map; + GUTF8String m(MessageFile); + errors=getbodies(paths,m,body,map); + } + if(! body.isempty()) + { + lt_XMLTags::get_Maps(messagestring,namestring,body,retval); + } + return errors; +} + + +const DjVuMessageLite & +DjVuMessage::create_full(void) +{ + GP<DjVuMessageLite> &static_message=getDjVuMessageLite(); + if(!static_message) + { + DjVuMessage *mesg=new DjVuMessage; + static_message=mesg; + mesg->init(); + } + return DjVuMessageLite::create_lite(); +} + +void +DjVuMessage::set_programname(const GUTF8String &xprogramname) +{ + programname()=xprogramname; + DjVuMessageLite::create=create_full; +} + +void +DjVuMessage::use_language(void) +{ + DjVuMessageLite::create=create_full; +} + + +// Constructor +DjVuMessage::DjVuMessage( void ) {} + +void +DjVuMessage::init(void) +{ + errors=parse(Map); +} + +// Destructor +DjVuMessage::~DjVuMessage( ) +{ +} + + +// A C function to perform a message lookup. Arguments are a buffer to receiv +// translated message, a buffer size (bytes), and a message_list. The transla +// result is returned in msg_buffer encoded in Native MBS encoding. In case +// of error, msg_b empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLookUpNative( + char *msg_buffer, const unsigned int buffer_size, const char *message) +{ + const GNativeString converted(DjVuMessage::LookUpNative( message )); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + +// A C function to perform a message lookup. Arguments are a buffer to receiv +// translated message, a buffer size (bytes), and a message_list. The transla +// result is returned in msg_buffer encoded in UTF8 encoding. In case +// of error, msg_b empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLookUpUTF8( + char *msg_buffer, const unsigned int buffer_size, const char *message) +{ + const GUTF8String converted(DjVuMessage::LookUpUTF8( message )); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuFormatErrorUTF8( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GUTF8String message(fmt,args); + DjVuWriteError( message ); +} + +void +DjVuFormatErrorNative( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + const GNativeString message(fmt,args); + DjVuWriteError( message ); +} + +const char * +djvu_programname(const char *xprogramname) +{ + if(xprogramname) + DjVuMessage::programname()=GNativeString(xprogramname); + return DjVuMessage::programname(); +} diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h new file mode 100644 index 00000000..2302be37 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessage.h @@ -0,0 +1,135 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessage.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + + +#ifndef __DJVU_MESSAGE_H__ +#define __DJVU_MESSAGE_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + + +#include "DjVuMessageLite.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GURL; + +class DjVuMessage : public DjVuMessageLite +{ +protected: + void init(void); + DjVuMessage(void); + +public: + /// Use the locale info and find the XML files on disk. + static void use_language(void); + + /// Set the program name used when searching for XML files on disk. + static void set_programname(const GUTF8String &programname); + static GUTF8String &programname(void); + + /// creates this class specifically. + static const DjVuMessageLite &create_full(void); + + /** Adds a byte stream to be parsed whenever the next DjVuMessage::create() + call is made. */ + static void AddByteStreamLater(const GP<ByteStream> &bs) + { use_language(); DjVuMessageLite::AddByteStreamLater(bs); } + + /** Destructor: Does any necessary cleanup. Actions depend on how the message + file is implemented. */ + ~DjVuMessage(); + + //// Same as LookUp, but this is a static method. + static GUTF8String LookUpUTF8( const GUTF8String & MessageList ) + { use_language();return DjVuMessageLite::LookUpUTF8(MessageList); } + + /** Same as Lookup, but returns a multibyte character string in the + current locale. */ + static GNativeString LookUpNative( const GUTF8String & MessageList ) + { use_language();return DjVuMessageLite::LookUpNative(MessageList); } + + /// This is a simple alias to the above class, but does an fprintf to stderr. + static void perror( const GUTF8String & MessageList ) + { use_language();DjVuMessageLite::perror(MessageList); } + + static GList<GURL> GetProfilePaths(void); +}; + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* __DJVU_MESSAGE_H__ */ + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp new file mode 100644 index 00000000..258b0649 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp @@ -0,0 +1,478 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessageLite.cpp,v 1.13 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + +#include "DjVuMessageLite.h" +#include "GOS.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GURL.h" +#include "debug.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +// #include <stdio.h> +#ifdef WIN32 +#include <tchar.h> +#include <atlbase.h> +#include <windows.h> +#include <winreg.h> +#endif +#ifdef UNIX +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#endif +#include <locale.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +const DjVuMessageLite& (*DjVuMessageLite::create)(void) = + DjVuMessageLite::create_lite; + +static const char *failed_to_parse_XML= + ERR_MSG("DjVuMessage.failed_to_parse_XML"); +static const char *unrecognized= + ERR_MSG("DjVuMessage.Unrecognized"); +static const char *uparameter= + ERR_MSG("DjVuMessage.Parameter"); +#ifdef LIZARDTECH_1_800_NUMBER +static const char unrecognized_default[] = + "** Unrecognized DjVu Message: [Contact LizardTech at " + LIZARDTECH_1_800_NUMBER " \n" + "\t** Message name: %1!s!"; +#else +static const char unrecognized_default[] = + "** Unrecognized DjVu Message:\n" + "\t** Message name: %1!s!"; +#endif +static const char uparameter_default[] = + "\t Parameter: %1!s!"; +static const char failed_to_parse_XML_default[]= + "Failed to parse XML message file: 	'%1!s!'."; + +static const char namestring[]="name"; +static const char valuestring[]="value"; +static const char numberstring[]="number"; +static const char bodystring[]="BODY"; +static const char messagestring[]="MESSAGE"; + +GPList<ByteStream> & +DjVuMessageLite::getByteStream(void) +{ + static GPList<ByteStream> gbs; + return gbs; +} + +GP<DjVuMessageLite> & +DjVuMessageLite::getDjVuMessageLite(void) +{ + static GP<DjVuMessageLite> message; + return message; +} + +void +DjVuMessageLite::AddByteStreamLater(const GP<ByteStream> &bs) +{ + getByteStream().append(bs); +} + +// There is only object of class DjVuMessage in a program, and here it is: +//DjVuMessage DjVuMsg; +const DjVuMessageLite & +DjVuMessageLite::create_lite(void) +{ + GP<DjVuMessageLite> &static_message=getDjVuMessageLite(); + if(!static_message) + { + static_message=new DjVuMessageLite; + } + DjVuMessageLite &m=*static_message; + GPList<ByteStream> &bs = getByteStream(); + for(GPosition pos;(pos=bs);bs.del(pos)) + { + m.AddByteStream(bs[pos]); + } + return m; +} + +// Constructor +DjVuMessageLite::DjVuMessageLite( void ) {} + +// Destructor +DjVuMessageLite::~DjVuMessageLite( ) {} + + +void +DjVuMessageLite::perror( const GUTF8String & MessageList ) +{ + DjVuPrintErrorUTF8("%s\n",(const char *)DjVuMessageLite::LookUpUTF8(MessageList)); +} + + +// Expands message lists by looking up the message IDs and inserting +// arguments into the retrieved messages. +// N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 Annex R) +// and SHOULD NOT BE ASSUMED TO BE ASCII. +GUTF8String +DjVuMessageLite::LookUp( const GUTF8String & MessageList ) const +{ +// DEBUG_MSG( "========== DjVuMessageLite::LookUp ==========\n" << +// MessageList << +// "\n========== DjVuMessageLite::LookUp ==========\n" ); + GUTF8String result; // Result string; begins empty + if(errors.length()) + { + const GUTF8String err1(errors); + (const_cast<GUTF8String &>(errors)).empty(); + result=LookUp(err1)+"\n"; + } + + int start = 0; // Beginning of next message + int end = MessageList.length(); // End of the message string + + // Isolate single messages and process them + while( start < end ) + { + if( MessageList[start] == '\n' ) + { + result += MessageList[start++]; // move the newline to the result + // and advance to the next message + } + else + { + // Find the end of the next message and process it + int next_ending = MessageList.search((unsigned long)'\n', start); + if( next_ending < 0 ) + next_ending = end; + result += LookUpSingle( MessageList.substr(start, next_ending-start) ); + // Advance to the next message + start = next_ending; + } + } + + // All done + return result; +} + + +// Expands a single message and inserts the arguments. Single_Message +// contains no separators (newlines), but includes all the parameters +// separated by tabs. +GUTF8String +DjVuMessageLite::LookUpSingle( const GUTF8String &Single_Message ) const +{ +#if HAS_CTRL_C_IN_ERR_MSG + if (Single_Message[0] != '\003') + return Single_Message; +#endif + // Isolate the message ID and get the corresponding message text + int ending_posn = Single_Message.contains("\t\v"); + if( ending_posn < 0 ) + ending_posn = Single_Message.length(); + GUTF8String msg_text; + GUTF8String msg_number; + const GUTF8String message=Single_Message.substr(0,ending_posn); + LookUpID( message, msg_text, msg_number ); + + // Check whether we found anything + if( !msg_text.length()) + { + if(message == unrecognized) + { + msg_text = unrecognized_default; + }else if(message == uparameter) + { + msg_text = uparameter_default; + }else if(message == failed_to_parse_XML) + { + msg_text = failed_to_parse_XML_default; + }else + { + return LookUpSingle(unrecognized + ("\t" + Single_Message)); + } + } + + // Insert the parameters (if any) + unsigned int param_num = 0; + while( (unsigned int)ending_posn < Single_Message.length() ) + { + GUTF8String arg; + const int start_posn = ending_posn+1; + if(Single_Message[ending_posn] == '\v') + { + ending_posn=Single_Message.length(); + arg=LookUpSingle(Single_Message.substr(start_posn,ending_posn)); + }else + { + ending_posn = Single_Message.contains("\v\t",start_posn); + if( ending_posn < 0 ) + ending_posn = Single_Message.length(); + arg=Single_Message.substr(start_posn, ending_posn-start_posn); + } + InsertArg( msg_text, ++param_num, arg); + } + // Insert the message number + InsertArg( msg_text, 0, msg_number ); + + return msg_text; +} + + +// Looks up the msgID in the file of messages and returns a pointer to +// the beginning of the translated message, if found; and an empty string +// otherwise. +void +DjVuMessageLite::LookUpID( const GUTF8String &xmsgID, + GUTF8String &message_text, + GUTF8String &message_number ) const +{ + if(!Map.isempty()) + { + GUTF8String msgID = xmsgID; +#if HAS_CTRL_C_IN_ERR_MSG + int start = 0; + while (msgID[start] == '\003') + start ++; + if (start > 0) + msgID = msgID.substr(start, msgID.length() - start); +#endif + GPosition pos=Map.contains(msgID); + if(pos) + { + const GP<lt_XMLTags> tag=Map[pos]; + GPosition valuepos=tag->get_args().contains(valuestring); + if(valuepos) + { + message_text=tag->get_args()[valuepos]; + }else + { + const GUTF8String raw(tag->get_raw()); + const int start_line=raw.search((unsigned long)'\n',0); + + const int start_text=raw.nextNonSpace(0); + const int end_text=raw.firstEndSpace(0); + if(start_line<0 || start_text<0 || start_text < start_line) + { + message_text=raw.substr(0,end_text).fromEscaped(); + }else + { + message_text=raw.substr(start_line+1,end_text-start_line-1).fromEscaped(); + } + } + GPosition numberpos=tag->get_args().contains(numberstring); + if(numberpos) + { + message_number=tag->get_args()[numberpos]; + } + } + } +} + + +// Insert a string into the message text. Will insert into any field +// description. Except for an ArgId of zero (message number), if the ArgId +// is not found, the routine adds a line with the parameter so information +// will not be lost. +void +DjVuMessageLite::InsertArg( GUTF8String &message, + const int ArgId, const GUTF8String &arg ) const +{ + // argument target string + const GUTF8String target= "%"+GUTF8String(ArgId)+"!"; + // location of target string + int format_start = message.search( (const char *)target ); + if( format_start >= 0 ) + { + do + { + const int n=format_start+target.length()+1; + const int format_end=message.search((unsigned long)'!',n); + if(format_end > format_start) + { + const int len=1+format_end-n; + if(len && isascii(message[n-1])) + { + GUTF8String narg; + GUTF8String format="%"+message.substr(n-1,len); + switch(format[len]) + { + case 'd': + case 'i': + narg.format((const char *)format,arg.toInt()); + break; + case 'u': + case 'o': + case 'x': + case 'X': + narg.format((const char *)format,(unsigned int)arg.toInt()); + break; + case 'f': + { + int endpos; + narg.format((const char *)format, arg.toDouble(0,endpos)); + if( endpos < 0 ) + narg = arg; + } + break; + default: + narg.format((const char *)format,(const char *)arg); + break; + } + message = message.substr( 0, format_start )+narg + +message.substr( format_end+1, -1 ); + }else + { + message = message.substr( 0, format_start )+arg + +message.substr( format_end+1, -1 ); + } + } + format_start=message.search((const char*)target, format_start+arg.length()); + } while(format_start >= 0); + } + else + { + // Not found, fake it + if( ArgId != 0 ) + { + message += "\n"+LookUpSingle(uparameter+("\t"+arg)); + } + } +} + + +// A C function to perform a message lookup. Arguments are a buffer to received the +// translated message, a buffer size (bytes), and a message_list. The translated +// result is returned in msg_buffer encoded in UTF-8. In case of error, msg_buffer is +// empty (i.e., msg_buffer[0] == '\0'). +void +DjVuMessageLite_LookUp( char *msg_buffer, const unsigned int buffer_size, const char *message ) +{ + GUTF8String converted = DjVuMessageLite::LookUpUTF8( message ); + if( converted.length() >= buffer_size ) + msg_buffer[0] = '\0'; + else + strcpy( msg_buffer, converted ); +} + +void +DjVuMessageLite::AddByteStream(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> gtags(lt_XMLTags::create(bs)); + lt_XMLTags &tags=*gtags; + GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring); + if(! Bodies.isempty()) + { + lt_XMLTags::get_Maps(messagestring,namestring,Bodies,Map); + } +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +void +DjVuWriteError( const char *message ) +{ + G_TRY { + GP<ByteStream> errout = ByteStream::get_stderr(); + if (errout) + { + const GUTF8String external = DjVuMessageLite::LookUpUTF8( message ); + errout->writestring(external+"\n"); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} + +void +DjVuWriteMessage( const char *message ) +{ + G_TRY { + GP<ByteStream> strout = ByteStream::get_stdout(); + if (strout) + { + const GUTF8String external = DjVuMessageLite::LookUpUTF8( message ); + strout->writestring(external+"\n"); + } + // Need to catch all exceptions because these might be + // called from an outer exception handler (with prejudice) + } G_CATCH_ALL { } G_ENDCATCH; +} diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h new file mode 100644 index 00000000..f2e941cf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h @@ -0,0 +1,227 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuMessageLite.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef __DJVU_MESSAGE_LITE_H__ +#define __DJVU_MESSAGE_LITE_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// All these I18N XML messages are Lizardtech innovations. +// For DjvuLibre, I changed the path extraction logic +// and added support for non I18N messages. + + +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class lt_XMLTags; +class ByteStream; + +/** Exception causes and external messages are passed as message lists which + have the following syntax: + + message_list ::= single_message | + single_message separator message_list + + separator ::= newline | + newline | separator + + single_message ::= message_ID | + message_ID parameters + + parameters ::= tab string | + tab string parameters + + Message_IDs are looked up an external file and replaced by the message + text strings they are mapped to. The message text may contain the + following: + + Parameter specifications: These are modelled after printf format + specifications and have one of the following forms: + + %n!s! %n!d! %n!i! %n!u! %n!x! %n!X! + + where n is the parameter number. The parameter number is indicated + explicitly to allow for the possibility that the parameter order may + change when the message text is translated into another language. + The final letter ('s', 'd', 'i', 'u', 'x', or 'X') indicates the form + of the parameter (string, integer, integer, unsigned integer, lowercase + hexadecimal, or uppercase hexadecimal, respectively). In addition + formating options such as precision available in sprintf, may be used. + So, to print the third argument as 5 digit unsigned number, with leading + zero's one could use: + %3!05u! + + All of the arguments are actually strings. For forms that don't take + string input ('d', 'i', 'u', 'x', or 'X'), and atoi() conversion is done + on the string before formatting. In addition the form indicates to the + translater whether to expect a word or a number. + + The strings are read in from XML. To to format the strings, use the + relavent XML escape sequences, such as follows: + + [line feed] + 	 [horizontal tab] + ' [single quote] + " [double quote] + < [less than sign] + > [greater than sign] + + After parameters have been inserted in the message text, the formatting + strings are replaced by their usual equivalents (newline and tab + respectively). + + If a message_id cannot be found in the external file, a message text + is fabricated giving the message_id and the parameters (if any). + + Separators (newlines) are preserved in the translated message list. + + Expands message lists by looking up the message IDs and inserting + arguments into the retrieved messages. + + N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 + Annex R) and SHOULD NOT BE ASSUMED TO BE ASCII. + */ + +class DjVuMessageLite : public GPEnabled +{ +protected: + // Constructor: + DjVuMessageLite( void ); + GMap<GUTF8String,GP<lt_XMLTags> > Map; + GUTF8String errors; + /// Creates a DjVuMessage class. + static const DjVuMessageLite &real_create(void); + +public: + /// Creates a DjVuMessage class. + static const DjVuMessageLite& (*create)(void); + + /// Creates this class specifically. + static const DjVuMessageLite &create_lite(void); + + /** Adds a byte stream to be parsed whenever the next DjVuMessage::create() + call is made. */ + static void AddByteStreamLater(const GP<ByteStream> &bs); + + /** Destructor: Does any necessary cleanup. Actions depend on how the message + file is implemented. */ + ~DjVuMessageLite(); + + /// Lookup the relavent string and parse the message. + GUTF8String LookUp( const GUTF8String & MessageList ) const; + + //// Same as LookUp, but this is a static method. + static GUTF8String LookUpUTF8( const GUTF8String & MessageList ) + { return create().LookUp(MessageList); } + + /** Same as Lookup, but returns the a multibyte character string in the + current locale. */ + static GNativeString LookUpNative( const GUTF8String & MessageList ) + { return create().LookUp(MessageList).getUTF82Native(); } + + /// This is a simple alias to the above class, but does an fprintf to stderr. + static void perror( const GUTF8String & MessageList ); + +protected: + + /* Looks up the msgID in the file of messages. The strings message_text + and message_number are returned if found. If not found, these strings + are empty. */ + void LookUpID( const GUTF8String & msgID, + GUTF8String &message_text, GUTF8String &message_number ) const; + + /* Expands a single message and inserts the arguments. Single_Message + contains no separators (newlines), but includes all the parameters + separated by tabs. */ + GUTF8String LookUpSingle( const GUTF8String & Single_Message ) const; + + /* Insert a string into the message text. Will insert into any field + description. Except for an ArgId of zero (message number), if the + #ArgId# is not found, the routine adds a line with the parameter + so information will not be lost. */ + void InsertArg( + GUTF8String &message, const int ArgId, const GUTF8String &arg ) const; + + void AddByteStream(const GP<ByteStream> &bs); + +protected: + /* Static storage of the DjVuMessage class. */ + static GP<DjVuMessageLite> &getDjVuMessageLite(void); + /* Static storage of the ByteStream list. */ + static GPList<ByteStream> &getByteStream(void); +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* __DJVU_MESSAGE_LITE_H__ */ + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp new file mode 100644 index 00000000..615041b0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp @@ -0,0 +1,237 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuNavDir.cpp,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuNavDir.h" +#include "debug.h" +#include "GException.h" +#include "GOS.h" +#include "ByteStream.h" +#include "GURL.h" +#include <ctype.h> + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +DjVuNavDir::DjVuNavDir(const GURL &dirURL) +{ + if (!dirURL) G_THROW( ERR_MSG("DjVuNavDir.zero_dir") ); + baseURL=dirURL.base(); +} + +DjVuNavDir::DjVuNavDir(ByteStream & str, const GURL &dirURL) +{ + if (!dirURL) G_THROW( ERR_MSG("DjVuNavDir.zero_dir") ); + + baseURL=GURL(dirURL).base(); + + decode(str); +} + +void +DjVuNavDir::decode(ByteStream & str) +{ + GCriticalSectionLock lk(&lock); + + GList<GUTF8String> tmp_page2name; + int eof=0; + while(!eof) + { + char buffer[1024]; + char * ptr; + for(ptr=buffer;ptr-buffer<1024;ptr++) + if ((eof=!str.read(ptr, 1)) || *ptr=='\n') break; + if (ptr-buffer==1024) G_THROW( ERR_MSG("DjVuNavDir.long_line") ); + *ptr=0; + if (!strlen(buffer)) continue; + + if (!tmp_page2name.contains(buffer)) + tmp_page2name.append(buffer); + }; + + // Now copying lists to arrays for faster access later + int pages=tmp_page2name.size(); + page2name.resize(pages-1); + + int cnt; + GPosition pos; + for(pos=tmp_page2name, cnt=0;pos;++pos, cnt++) + page2name[cnt]=tmp_page2name[pos]; + + // Now creating reverse mapping (strings => numbers) + for(cnt=0;cnt<pages;cnt++) + { + name2page[page2name[cnt]]=cnt; + url2page[GURL::UTF8(page2name[cnt],baseURL)]=cnt; + } +} + +#ifndef NEED_DECODER_ONLY +void +DjVuNavDir::encode(ByteStream & str) +{ + GCriticalSectionLock lk(&lock); + + for(int i=0;i<page2name.size();i++) + { + GUTF8String & name=page2name[i]; + str.writall((const char*)name, name.length()); + str.writall("\n", 1); + }; +} +#endif //NEED_DECODER_ONLY + +int +DjVuNavDir::get_pages_num(void) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + return page2name.size(); +} + +int +DjVuNavDir::name_to_page(const char * name) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (!name2page.contains(name)) return -1; + return name2page[name]; +} + +int +DjVuNavDir::url_to_page(const GURL & url) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (!url2page.contains(url)) return -1; + return url2page[url]; +} + +GUTF8String +DjVuNavDir::page_to_name(int page) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + if (page<0) + G_THROW( ERR_MSG("DjVuNavDir.neg_page") ); + if (page>=page2name.size()) + G_THROW( ERR_MSG("DjVuNavDir.large_page") ); + return page2name[page]; +} + +GURL +DjVuNavDir::page_to_url(int page) const +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + return GURL::UTF8(page_to_name(page),baseURL); +} + +void +DjVuNavDir::insert_page(int where, const char * name) +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + int pages=page2name.size(); + if (where<0) where=pages; + + page2name.resize(pages); + for(int i=pages;i>where;i--) + page2name[i]=page2name[i-1]; + page2name[where]=name; + name2page[name]=where; + url2page[GURL::UTF8(name,baseURL)]=where; +} + +#ifndef NEED_DECODER_ONLY +void +DjVuNavDir::delete_page(int page_num) +{ + GCriticalSectionLock lk((GCriticalSection *)&lock); + + int pages=page2name.size(); + + if (page_num<0 || page_num>=pages) + G_THROW( ERR_MSG("DjVuNavDir.bad_page") ); + + for(int i=page_num;i<pages-1;i++) + page2name[i]=page2name[i+1]; + page2name.resize(--pages-1); +} +#endif + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h new file mode 100644 index 00000000..90b2b8db --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h @@ -0,0 +1,192 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuNavDir.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUNAVDIR_H +#define _DJVUNAVDIR_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" +#include "GThreads.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + +/** @name DjVuNavDir.h + Files #"DjVuNavDir.h"# and #"DjVuNavDir.cpp"# contain implementation of the + multipage DjVu navigation directory. This directory lists all the pages, + that a given document is composed of. The navigation (switching from page + to page in the plugin) is not possible before this directory is decoded. + + Refer to the \Ref{DjVuNavDir} class description for greater details. + + @memo DjVu Navigation Directory + @author Andrei Erofeev <[email protected]> + @version #$Id: DjVuNavDir.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# +*/ + +//@{ + +//***************************************************************************** +//********************* Note: this class is thread-safe *********************** +//***************************************************************************** + +/** DjVu Navigation Directory. + + This class implements the {\em navigation directory} of a multipage + DjVu document - basically a list of pages that this document is composed + of. We would like to emphasize, that this is the list of namely + {\bf pages}, not {\bf files}. Any page may include any + number of additional files. When you've got an all-in-one-file multipage + DjVu document (DjVm archive) you may get the files list from \Ref{DjVmDir0} + class. + + The \Ref{DjVuNavDir} class can decode and encode the navigation directory + from {\bf NDIR} IFF chunk. It's normally created by the library during + decoding procedure and can be accessed like any other component of + the \Ref{DjVuImage} being decoded. + + In a typical multipage DjVu document the navigation directory is stored + in a separate IFF file containing only one chunk: {\bf NDIR} chunk. + This file should be included (by means of the {\bf INCL} chunk) into + every page of the document to enable the navigation. */ +class DjVuNavDir : public GPEnabled +{ +private: + GCriticalSection lock; + GURL baseURL; + GArray<GUTF8String> page2name; + GMap<GUTF8String, int> name2page; + GMap<GURL, int> url2page; +protected: + DjVuNavDir(const GURL &dir_url); + DjVuNavDir(ByteStream & str, const GURL &dir_url); + +public: + int get_memory_usage(void) const { return 1024; }; + + /** Creates a #DjVuNavDir# object. #dir_url# is the URL of the file + containing the directory source data. It will be used later + in translation by functions like \Ref{url_to_page}() and + \Ref{page_to_url}() */ + static GP<DjVuNavDir> create(const GURL &dir_url) + {return new DjVuNavDir(dir_url);} + + /** Creates #DjVuNavDir# object by decoding its contents from + the stream. #dir_url# is the URL of the file containing the + directory source data. */ + static GP<DjVuNavDir> create(ByteStream & str, const GURL &dir_url) + { return new DjVuNavDir(str,dir_url); } + + virtual ~DjVuNavDir(void) {}; + + /// Decodes the directory contents from the given \Ref{ByteStream} + void decode(ByteStream & str); + + /// Encodes the directory contents into the given \Ref{ByteStream} + void encode(ByteStream & str); + + /** Inserts a new page at position #where# pointing to a file + with name #name#. + + @param where The position where the page should be inserted. + #-1# means to append. + @param name The name of the file corresponding to this page. + The name may not contain slashes. The file may include + other files. */ + void insert_page(int where, const char * name); + + /// Deletes page with number #page_num# from the directory. + void delete_page(int page_num); + + /// Returns the number of pages in the directory. + int get_pages_num(void) const; + /** Converts the #url# to page number. Returns #-1# if the #url# + does not correspond to anything in the directory. */ + int url_to_page(const GURL & url) const; + /** Converts file name #name# to page number. Returns #-1# if file + with given name cannot be found. */ + int name_to_page(const char * name) const; + /** Converts given #page# to URL. Throws an exception if page number + is invalid. */ + GURL page_to_url(int page) const; + /** Converts given #page# to URL. Throws an exception if page number + is invalid. */ + GUTF8String page_to_name(int page) const; +}; + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp new file mode 100644 index 00000000..4e49fe12 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp @@ -0,0 +1,590 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPalette.cpp,v 1.11 2004/03/18 15:03:50 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GException.h" +#include "ByteStream.h" +#include "BSByteStream.h" +#include "DjVuPalette.h" +#include <stdlib.h> +#include <math.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define CUBEBITS 4 +#define CUBESIDE (1<<CUBEBITS) +#define CUBESIZE (CUBESIDE*CUBESIDE*CUBESIDE) + +#define RMUL 5 +#define GMUL 9 +#define BMUL 2 +#define SMUL (RMUL+GMUL+BMUL) + +#define MAXPALETTESIZE 65535 // Limit for a 16 bit unsigned read. + + +inline unsigned char +umax(unsigned char a, unsigned char b) +{ return (a>b) ? a : b; } + +inline unsigned char +umin(unsigned char a, unsigned char b) +{ return (a>b) ? b : a; } + +inline float +fmin(float a, float b) +{ return (a>b) ? b : a; } + + + +// ------- DJVUPALETTE + + +DjVuPalette::DjVuPalette() + : mask(0), hist(0), pmap(0) +{ +} + +DjVuPalette::~DjVuPalette() +{ + delete hist; + delete pmap; +} + +DjVuPalette& +DjVuPalette::operator=(const DjVuPalette &ref) +{ + if (this != &ref) + { + delete hist; + delete pmap; + mask = 0; + palette = ref.palette; + colordata = ref.colordata; + } + return *this; +} + +DjVuPalette::DjVuPalette(const DjVuPalette &ref) + : mask(0), hist(0), pmap(0) +{ + this->operator=(ref); +} + + + +// -------- HISTOGRAM ALLOCATION + +void +DjVuPalette::allocate_hist() +{ + if (! hist) + { + hist = new GMap<int,int>; + mask = 0; + } + else + { + GMap<int,int> *old = hist; + hist = new GMap<int,int>; + mask = (mask<<1)|(0x010101); + for (GPosition p = *old; p; ++p) + { + int k = old->key(p); + int w = (*old)[p]; + (*hist)[k | mask] += w; + } + delete old; + } +} + + +// -------- PALETTE COMPUTATION + + +#ifndef NEED_DECODER_ONLY + +struct PData +{ + unsigned char p[3]; + int w; +}; + +struct PBox +{ + PData *data; + int colors; + int boxsize; + int sum; +}; +int +DjVuPalette::bcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[0] - ((PData*)b)->p[0]; +} + +int + +DjVuPalette::gcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[1] - ((PData*)b)->p[1]; +} + +int +DjVuPalette::rcomp (const void *a, const void *b) +{ + return ((PData*)a)->p[2] - ((PData*)b)->p[2]; +} + +int +DjVuPalette::lcomp (const void *a, const void *b) +{ + unsigned char *aa = ((PColor*)a)->p; + unsigned char *bb = ((PColor*)b)->p; + if (aa[3] != bb[3]) + return aa[3]-bb[3]; + else if (aa[2] != bb[2]) + return aa[2]-bb[2]; + else if (aa[1] != bb[1]) + return aa[1]=bb[1]; + else + return aa[0]-bb[0]; +} + +int +DjVuPalette::compute_palette(int maxcolors, int minboxsize) +{ + if (!hist) + G_THROW( ERR_MSG("DjVuPalette.no_color") ); + if (maxcolors<1 || maxcolors>MAXPALETTESIZE) + G_THROW( ERR_MSG("DjVuPalette.many_colors") ); + + // Paul Heckbert: "Color Image Quantization for Frame Buffer Display", + // SIGGRAPH '82 Proceedings, page 297. (also in ppmquant) + + // Collect histogram colors + int sum = 0; + int ncolors = 0; + GTArray<PData> pdata; + for (GPosition p = *hist; p; ++p) + { + pdata.touch(ncolors); + PData &data = pdata[ncolors++]; + int k = hist->key(p); + data.p[0] = (k>>16) & 0xff; + data.p[1] = (k>>8) & 0xff; + data.p[2] = (k) & 0xff; + data.w = (*hist)[p]; + sum += data.w; + } + // Create first box + GList<PBox> boxes; + PBox newbox; + newbox.data = pdata; + newbox.colors = ncolors; + newbox.boxsize = 256; + newbox.sum = sum; + boxes.append(newbox); + // Repeat spliting boxes + while (boxes.size() < maxcolors) + { + // Find suitable box + GPosition p; + for (p=boxes; p; ++p) + if (boxes[p].colors>=2 && boxes[p].boxsize>minboxsize) + break; + if (! p) + break; + // Find box boundaries + PBox &splitbox = boxes[p]; + unsigned char pmax[3]; + unsigned char pmin[3]; + pmax[0] = pmin[0] = splitbox.data->p[0]; + pmax[1] = pmin[1] = splitbox.data->p[1]; + pmax[2] = pmin[2] = splitbox.data->p[2]; + for (int j=1; j<splitbox.colors; j++) + { + pmax[0] = umax(pmax[0], splitbox.data[j].p[0]); + pmax[1] = umax(pmax[1], splitbox.data[j].p[1]); + pmax[2] = umax(pmax[2], splitbox.data[j].p[2]); + pmin[0] = umin(pmin[0], splitbox.data[j].p[0]); + pmin[1] = umin(pmin[1], splitbox.data[j].p[1]); + pmin[2] = umin(pmin[2], splitbox.data[j].p[2]); + } + // Determine split direction and sort + int bl = pmax[0]-pmin[0]; + int gl = pmax[1]-pmin[1]; + int rl = pmax[2]-pmin[2]; + splitbox.boxsize = (bl>gl ? (rl>bl ? rl : bl) : (rl>gl ? rl : gl)); + if (splitbox.boxsize <= minboxsize) + continue; + if (gl == splitbox.boxsize) + qsort(splitbox.data, splitbox.colors, sizeof(PData), gcomp); + else if (rl == splitbox.boxsize) + qsort(splitbox.data, splitbox.colors, sizeof(PData), rcomp); + else + qsort(splitbox.data, splitbox.colors, sizeof(PData), bcomp); + // Find median + int lowercolors = 0; + int lowersum = 0; + while (lowercolors<splitbox.colors-1 && lowersum+lowersum<splitbox.sum) + lowersum += splitbox.data[lowercolors++].w; + // Compute new boxes + newbox.data = splitbox.data + lowercolors; + newbox.colors = splitbox.colors - lowercolors; + newbox.sum = splitbox.sum - lowersum; + splitbox.colors = lowercolors; + splitbox.sum = lowersum; + // Insert boxes at proper location + GPosition q; + for (q=p; q; ++q) + if (boxes[q].sum < newbox.sum) + break; + boxes.insert_before(q, newbox); + for (q=p; q; ++q) + if (boxes[q].sum < splitbox.sum) + break; + boxes.insert_before(q, boxes, p); + } + // Fill palette array + ncolors = 0; + palette.empty(); + palette.resize(0,boxes.size()-1); + for (GPosition p=boxes; p; ++p) + { + PBox &box = boxes[p]; + // Compute box representative color + float bsum = 0; + float gsum = 0; + float rsum = 0; + for (int j=0; j<box.colors; j++) + { + float w = (float)box.data[j].w; + bsum += box.data[j].p[0] * w; + gsum += box.data[j].p[1] * w; + rsum += box.data[j].p[2] * w; + } + PColor &color = palette[ncolors++]; + color.p[0] = (unsigned char) fmin(255, bsum/box.sum); + color.p[1] = (unsigned char) fmin(255, gsum/box.sum); + color.p[2] = (unsigned char) fmin(255, rsum/box.sum); + color.p[3] = ( color.p[0]*BMUL + color.p[1]*GMUL + color.p[2]*RMUL) / SMUL; + } + // Save dominant color + PColor dcolor = palette[0]; + // Sort palette colors in luminance order + qsort((PColor*)palette, ncolors, sizeof(PColor), lcomp); + // Clear invalid data + colordata.empty(); + delete pmap; + pmap = 0; + // Return dominant color + return color_to_index_slow(dcolor.p); +} + + + +int +DjVuPalette::compute_pixmap_palette(const GPixmap &pm, int ncolors, int minboxsize) +{ + // Prepare histogram + histogram_clear(); + for (int j=0; j<(int)pm.rows(); j++) + { + const GPixel *p = pm[j]; + for (int i=0; i<(int)pm.columns(); i++) + histogram_add(p[i], 1); + } + // Compute palette + return compute_palette(ncolors, minboxsize); +} + + +#endif + + + + +// -------- QUANTIZATION + + +void +DjVuPalette::allocate_pmap() +{ + if (! pmap) + pmap = new GMap<int,int>; +} + +int +DjVuPalette::color_to_index_slow(const unsigned char *bgr) +{ + PColor *pal = palette; + const int ncolors = palette.size(); + if (! ncolors) + G_THROW( ERR_MSG("DjVuPalette.not_init") ); + // Should be able to do better + int found = 0; + int founddist = 3*256*256; + for (int i=0; i<ncolors; i++) + { + int bd = bgr[0] - pal[i].p[0]; + int gd = bgr[1] - pal[i].p[1]; + int rd = bgr[2] - pal[i].p[2]; + int dist = (bd*bd)+(gd*gd)+(rd*rd); + if (dist < founddist) + { + found = i; + founddist = dist; + } + } + // Store in pmap + if (pmap && pmap->size()<0x8000) + { + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2]); + (*pmap)[key] = found; + } + // Return + return found; +} + + +#ifndef NEED_DECODER_ONLY + +void +DjVuPalette::quantize(GPixmap &pm) +{ + for (int j=0; j<(int)pm.rows(); j++) + { + GPixel *p = pm[j]; + for (int i=0; i<(int)pm.columns(); i++) + index_to_color(color_to_index(p[i]), p[i]); + } +} + +int +DjVuPalette::compute_palette_and_quantize(GPixmap &pm, int maxcolors, int minboxsize) +{ + int result = compute_pixmap_palette(pm, maxcolors, minboxsize); + quantize(pm); + return result; +} + +void +DjVuPalette::color_correct(double corr) +{ + const int palettesize = palette.size(); + if (palettesize > 0) + { + // Copy colors + int i; + GTArray<GPixel> pix(0,palettesize-1); + GPixel *r = pix; + PColor *q = palette; + for (i=0; i<palettesize; i++) + { + r[i].b = q[i].p[0]; + r[i].g = q[i].p[1]; + r[i].r = q[i].p[2]; + } + // Apply color correction + GPixmap::color_correct(corr, r, palettesize); + // Restore colors + for (i=0; i<palettesize; i++) + { + q[i].p[0] = r[i].b; + q[i].p[1] = r[i].g; + q[i].p[2] = r[i].r; + } + } +} + +#endif + + +// -------- ENCODE AND DECODE + +#define DJVUPALETTEVERSION 0 + +void +DjVuPalette::encode_rgb_entries(ByteStream &bs) const +{ + const int palettesize = palette.size(); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + p[2] = palette[c].p[0]; + p[1] = palette[c].p[1]; + p[0] = palette[c].p[2]; + bs.writall((const void*)p, 3); + } +} + +void +DjVuPalette::encode(GP<ByteStream> gbs) const +{ + ByteStream &bs=*gbs; + const int palettesize = palette.size(); + const int datasize = colordata.size(); + // Code version number + int version = DJVUPALETTEVERSION; + if (datasize>0) version |= 0x80; + bs.write8(version); + // Code palette + bs.write16(palettesize); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + p[0] = palette[c].p[0]; + p[1] = palette[c].p[1]; + p[2] = palette[c].p[2]; + bs.writall((const void*)p, 3); + } + // Code colordata + if (datasize > 0) + { + bs.write24(datasize); + GP<ByteStream> gbsb=BSByteStream::create(gbs, 50); + ByteStream &bsb=*gbsb; + for (int d=0; d<datasize; d++) + bsb.write16(colordata[d]); + } +} + +void +DjVuPalette::decode_rgb_entries(ByteStream &bs, const int palettesize) +{ + palette.resize(0,palettesize-1); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + bs.readall((void*)p, 3); + palette[c].p[0] = p[2]; + palette[c].p[1] = p[1]; + palette[c].p[2] = p[0]; + palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL; + } +} + +void +DjVuPalette::decode(GP<ByteStream> gbs) +{ + ByteStream &bs=*gbs; + // Make sure that everything is clear + delete hist; + delete pmap; + hist = 0; + pmap = 0; + mask = 0; + // Code version + int version = bs.read8(); + if ( (version & 0x7f) != DJVUPALETTEVERSION) + G_THROW( ERR_MSG("DjVuPalette.bad_version") ); + // Code palette + const int palettesize = bs.read16(); + if (palettesize<0 || palettesize>MAXPALETTESIZE) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + palette.resize(0,palettesize-1); + for (int c=0; c<palettesize; c++) + { + unsigned char p[3]; + bs.readall((void*)p, 3); + palette[c].p[0] = p[0]; + palette[c].p[1] = p[1]; + palette[c].p[2] = p[2]; + palette[c].p[3] = (p[0]*BMUL+p[1]*GMUL+p[2]*RMUL)/SMUL; + } + // Code data + if (version & 0x80) + { + int datasize = bs.read24(); + if (datasize<0) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + colordata.resize(0,datasize-1); + GP<ByteStream> gbsb=BSByteStream::create(gbs); + ByteStream &bsb=*gbsb; + for (int d=0; d<datasize; d++) + { + short s = bsb.read16(); + if (s<0 || s>=palettesize) + G_THROW( ERR_MSG("DjVuPalette.bad_palette") ); + colordata[d] = s; + } + } +} + + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h new file mode 100644 index 00000000..7f9884f1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPalette.h @@ -0,0 +1,339 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPalette.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUPALETTE_H_ +#define _DJVUPALETTE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GContainer.h" +#include "GPixmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name DjVuPalette.h + Files #"DjVuPalette.h"# and #"DjVuPalette.cpp"# implement a single class + \Ref{DjVuPalette} which provides facilities for computing optimal color + palettes, coding color palettes, and coding sequences of color indices. + @memo + DjVuPalette header file + @version + #$Id: DjVuPalette.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# + @author: + L\'eon Bottou <[email protected]> */ +//@{ + + +/** Computing and coding color palettes and index arrays. + This class provides facilities for computing optimal color palettes, + coding color palettes, and coding sequences of color indices. + + {\bf Creating a color palette} -- The recipe for creating a color palette + consists in (a) creating a DjVuPalette object, (b) constructing a color + histogram using \Ref{histogram_add}, and (c) calling function + \Ref{compute_palette}. + + {\bf Accessing the color palette} -- Conversion between colors and color + palette indices is easily achieved with \Ref{color_to_index} and + \Ref{index_to_color}. There are also functions for computing a palette + and quantizing a complete pixmap. + + {\bf Sequences of color indices} -- The DjVuPalette object also contains + an array \Ref{colordata} optionally containing a sequence of color + indices. This array will be encoded and decoded by functions \Ref{encode} + and \Ref{decode}. This feature simplifies the implementation of the ``one + color per symbol'' model in DjVu. + + {\bf Coding color palettes and color indices} -- Two functions + \Ref{encode} and \Ref{decode} are provided for coding the color palette + and the array of color indices when appropriate. */ +#ifdef _WIN32_WCE_EMULATION // Work around odd behavior under WCE Emulation +#define CALLINGCONVENTION __cdecl +#else +#define CALLINGCONVENTION /* */ +#endif + +class DjVuPalette : public GPEnabled +{ +protected: + DjVuPalette(void); +public: + /// Generic creator + static GP<DjVuPalette> create(void) {return new DjVuPalette();} + + /// Non-virtual destructor + ~DjVuPalette(); + // COPY + DjVuPalette(const DjVuPalette &ref); + DjVuPalette& operator=(const DjVuPalette &ref); + // PALETTE COMPUTATION + /** Resets the color histogram to zero. */ + void histogram_clear(); + /** Adds the color specified by #p# to the histogram. + Argument #weight# represent the number of pixels with this color. */ + void histogram_add(const GPixel &p, int weight); + /** Adds the color specified by the triple #bgr# to the histogram. + Argument #weight# represent the number of pixels with this color. */ + void histogram_add(const unsigned char *bgr, int weight); + /** Adds the color specified by the weighted triple #bgr# to the histogram. + Argument #weight# represent the number of pixels with this color. This + function will compute the actual color by dividing the elements of the + #bgr# array by #weight# and then use the unnormalized values to compute + the average color per bucket. This is all a way to avoid excessive loss + of accuracy. */ + void histogram_norm_and_add(const int *bgr, int weight); + /** Computes an optimal palette for representing an image where colors + appear according to the histogram. Argument #maxcolors# is the maximum + number of colors allowed in the palette (up to 1024). Argument + #minboxsize# controls the minimal size of the color cube area affected + to a color palette entry. Returns the index of the dominant color. */ + int compute_palette(int maxcolors, int minboxsize=0); + /** Computes the optimal palette for pixmap #pm#. This function builds the + histogram for pixmap #pm# and computes the optimal palette using + \Ref{compute_palette}. */ + int compute_pixmap_palette(const GPixmap &pm, int ncolors, int minboxsize=0); + // CONVERSION + /** Returns the number of colors in the palette. */ + int size() const; + /** Returns the best palette index for representing color #p#. */ + int color_to_index(const GPixel &p); + /** Returns the best palette index for representing color #bgr#. */ + int color_to_index(const unsigned char *bgr); + /** Overwrites #p# with the color located at position #index# in the palette. */ + void index_to_color(int index, GPixel &p) const; + /** Overwrites #rgb[0..3]# with the color located at + position #index# in the palette. */ + void index_to_color(int index, unsigned char *bgr) const; + /** Quantizes pixmap #pm#. All pixels are replaced by their closest + approximation available in the palette. */ + void quantize(GPixmap &pm); + /** Calls \Ref{compute_pixmap_palette} and \Ref{quantize}. */ + int compute_palette_and_quantize(GPixmap &pm, int maxcolors, int minboxsize=0); + // COLOR CORRECTION + /** Applies a luminance gamma correction factor of #corr# to the palette + entries. Values greater than #1.0# make the image brighter. Values + smaller than #1.0# make the image darker. The documentation of program + \Ref{ppmcoco} explains how to properly use this function. */ + void color_correct(double corr); + // COLOR INDEX DATA + /** Contains an optional sequence of color indices. + Function \Ref{encode} and \Ref{decode} also encode and decode this + sequence when such a sequence is provided. */ + GTArray<short> colordata; + /** Returns colors from the color index sequence. Pixel #out# is + overwritten with the color corresponding to the #nth# element of the + color sequence \Ref{colordata}. */ + void get_color(int nth, GPixel &out) const; + // CODING + /** Writes the palette colors. This function writes each palette color as a + RGB triple into bytestream #bs#. */ + void encode_rgb_entries(ByteStream &bs) const; + /** Reads palette colors. This function initializes the palette colors by + reading #palettesize# RGB triples from bytestream #bs#. */ + void decode_rgb_entries(ByteStream &bs, const int palettesize); + /** Encodes the palette and the color index sequence. This function encodes + the a version byte, the palette size, the palette colors and the color + index sequence into bytestream #bs#. Note that the color histogram is + never saved. */ + void encode(GP<ByteStream> bs) const; + /** Initializes the object by reading data from bytestream #bs#. This + function reads a version byte, the palette size, the palette and the + color index sequence from bytestream #bs#. Note that the color + histogram is never saved. */ + void decode(GP<ByteStream> bs); + +private: + // Histogram + int mask; + GMap<int,int> *hist; + // Quantization data + struct PColor { unsigned char p[4]; }; + GTArray<PColor> palette; + GMap<int,int> *pmap; + // Helpers + void allocate_hist(); + void allocate_pmap(); + static int CALLINGCONVENTION bcomp (const void*, const void*); + static int CALLINGCONVENTION gcomp (const void*, const void*); + static int CALLINGCONVENTION rcomp (const void*, const void*); + static int CALLINGCONVENTION lcomp (const void*, const void*); + int color_to_index_slow(const unsigned char *bgr); +private: // dummy functions + static void encode(ByteStream *); + static void decode(ByteStream *); +}; + + +//@} + +// ------------ INLINES + + +inline void +DjVuPalette::histogram_clear() +{ + delete hist; + hist = 0; + mask = 0; +} + +inline void +DjVuPalette::histogram_add(const unsigned char *bgr, int weight) +{ + if (weight > 0) + { + if (!hist || hist->size()>=0x4000) + allocate_hist(); + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2])|(mask); + (*hist)[key] += weight; + } +} + +inline void +DjVuPalette::histogram_add(const GPixel &p, int weight) +{ + histogram_add(&p.b, weight); +} + +inline void +DjVuPalette::histogram_norm_and_add(const int *bgr, int weight) +{ + if (weight > 0) + { + int p0 = bgr[0]/weight; if (p0>255) p0=255; + int p1 = bgr[1]/weight; if (p1>255) p1=255; + int p2 = bgr[2]/weight; if (p2>255) p2=255; + if (!hist || hist->size()>=0x4000) + allocate_hist(); + int key = (p0<<16)|(p1<<8)|(p2)|(mask); + (*hist)[key] += weight; + } +} + +inline int +DjVuPalette::size() const +{ + return palette.size(); +} + +inline int +DjVuPalette::color_to_index(const unsigned char *bgr) +{ + if (! pmap) + allocate_pmap(); + int key = (bgr[0]<<16)|(bgr[1]<<8)|(bgr[2]); + GPosition p = pmap->contains(key); + if ( p) + return (*pmap)[p]; + return color_to_index_slow(bgr); +} + +inline int +DjVuPalette::color_to_index(const GPixel &p) +{ + return color_to_index(&p.b); +} + +inline void +DjVuPalette::index_to_color(int index, unsigned char *bgr) const +{ + const PColor &color = palette[index]; + bgr[0] = color.p[0]; + bgr[1] = color.p[1]; + bgr[2] = color.p[2]; +} + +inline void +DjVuPalette::index_to_color(int index, GPixel &p) const +{ + index_to_color(index, &p.b); +} + +inline void +DjVuPalette::get_color(int nth, GPixel &p) const +{ + index_to_color(colordata[nth], p); +} + + + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp new file mode 100644 index 00000000..5e8a25c9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp @@ -0,0 +1,710 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPort.cpp,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuPort.h" +#include "GOS.h" +#include "DjVuImage.h" +#include "DjVuDocument.h" +#include "DjVuFile.h" +#include "DjVuMessageLite.h" +#include "DataPool.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +//**************************************************************************** +//******************************* Globals ************************************ +//**************************************************************************** + +static DjVuPortcaster *pcaster; + +DjVuPortcaster * +DjVuPort::get_portcaster(void) +{ + if (!pcaster) pcaster = new DjVuPortcaster(); + return pcaster; +} + +class DjVuPort::DjVuPortCorpse +{ +public: + DjVuPort * port; + DjVuPortCorpse * next; + + DjVuPortCorpse(DjVuPort * _port) : port(_port), next(0) {} +}; + +//**************************************************************************** +//******************************* DjVuPort *********************************** +//**************************************************************************** + +#define MAX_CORPSE_NUM 128 + +// Last MAX_CORPSE_NUM addresses of dead DjVuPorts. We want to maintain this +// list because of the way DjVuPort::is_port_alive() works: it accepts an +// address and runs it thru its internal maps. The problem will occur if +// a new DjVuPort is created exactly on place of another one, which just +// died. Here we attempt to remember the last MAX_CORPSE_NUM addresses +// of dead DjVuPorts, and take them into account in DjVuPort::operator new(); +GCriticalSection * DjVuPort::corpse_lock; +DjVuPort::DjVuPortCorpse * DjVuPort::corpse_head; +DjVuPort::DjVuPortCorpse * DjVuPort::corpse_tail; +int DjVuPort::corpse_num; + +void * +DjVuPort::operator new (size_t sz) +{ + if (!corpse_lock) corpse_lock=new GCriticalSection(); + + // Loop until we manage to allocate smth, which is not mentioned in + // the 'corpse' list. Thus we will avoid allocating a new DjVuPort + // on place of a dead one. Not *absolutely* secure (only 64 items + // in the list) but is still better than nothing. + void * addr=0; + { + GCriticalSectionLock lock(corpse_lock); + + // Store here addresses, which were found in 'corpse' list. + // We will free then in the end + int addr_num=0; + static void * addr_arr[MAX_CORPSE_NUM]; + + // Make at most MAX_CORPSE_NUM attempts. During each attempt + // we try to allocate a block of memory for DjVuPort. If + // the address of this block is not in the corpse list, we break + // All addresses will be recorder, so that we can delete them + // after we're done. + for(int attempt=0;attempt<MAX_CORPSE_NUM;attempt++) + { + void * test_addr=::operator new (sz); + addr_arr[addr_num++]=test_addr; + + // See if 'test_addr' is in the 'corpse' list (was recently used) + DjVuPortCorpse * corpse; + for(corpse=corpse_head;corpse;corpse=corpse->next) + if (test_addr==corpse->port) break; + if (!corpse) + { + addr=test_addr; + addr_num--; + break; + } + } + // If all attempts failed (all addresses generated are already + // in the list of corpses, allocate a new one and proceed + // w/o additional checks + if (!addr) addr=::operator new(sz); + + // Here 'addr_arr[0<=i<addr_num]' contains addresses, that we + // tried to allocate, and which need to be freed now + // 'addr' contains address we want to use. + addr_num--; + while(addr_num>=0) ::operator delete(addr_arr[addr_num--]); + } + + DjVuPortcaster * pcaster=get_portcaster(); + GCriticalSectionLock lock(&pcaster->map_lock); + pcaster->cont_map[addr]=0; + return addr; +} + +void +DjVuPort::operator delete(void * addr) +{ + if (corpse_lock) + { + GCriticalSectionLock lock(corpse_lock); + + // Add 'addr' to the list of corpses + if (corpse_tail) + { + corpse_tail->next=new DjVuPortCorpse((DjVuPort *) addr); + corpse_tail=corpse_tail->next; + corpse_tail->next=0; + } else + { + corpse_head=corpse_tail=new DjVuPortCorpse((DjVuPort *) addr); + corpse_tail->next=0; + } + corpse_num++; + if (corpse_num>=MAX_CORPSE_NUM) + { + DjVuPortCorpse * corpse=corpse_head; + corpse_head=corpse_head->next; + delete corpse; + corpse_num--; + } + } + ::operator delete(addr); +} + +DjVuPort::DjVuPort() +{ + DjVuPortcaster *pcaster = get_portcaster(); + GCriticalSectionLock lock(& pcaster->map_lock ); + GPosition p = pcaster->cont_map.contains(this); + if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") ); + pcaster->cont_map[p] = (void*)this; +} + +DjVuPort::DjVuPort(const DjVuPort & port) +{ + DjVuPortcaster *pcaster = get_portcaster(); + GCriticalSectionLock lock(& pcaster->map_lock ); + GPosition p = pcaster->cont_map.contains(this); + if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") ); + pcaster->cont_map[p] = (void*)this; + pcaster->copy_routes(this, &port); +} + +DjVuPort & +DjVuPort::operator=(const DjVuPort & port) +{ + if (this != &port) + get_portcaster()->copy_routes(this, &port); + return *this; +} + +DjVuPort::~DjVuPort(void) +{ + get_portcaster()->del_port(this); +} + + +//**************************************************************************** +//**************************** DjVuPortcaster ******************************** +//**************************************************************************** + + + +DjVuPortcaster::DjVuPortcaster(void) +{ +} + +DjVuPortcaster::~DjVuPortcaster(void) +{ + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=route_map;pos;++pos) + delete (GList<void *> *) route_map[pos]; +} + +GP<DjVuPort> +DjVuPortcaster::is_port_alive(DjVuPort *port) +{ + GP<DjVuPort> gp_port; + GCriticalSectionLock lock(&map_lock); + GPosition pos=cont_map.contains(port); + if (pos && cont_map[pos] && ((DjVuPort *) port)->get_count()>0) + gp_port=port; + return gp_port; +} + +void +DjVuPortcaster::add_alias(const DjVuPort * port, const GUTF8String &alias) +{ + GCriticalSectionLock lock(&map_lock); + a2p_map[alias]=port; +} + +void +DjVuPortcaster::clear_all_aliases(void) +{ + DjVuPortcaster *p=get_portcaster(); + GCriticalSectionLock lock(&(p->map_lock)); + GPosition pos; + while((pos=p->a2p_map)) + { + p->a2p_map.del(pos); + } +} + +void +DjVuPortcaster::clear_aliases(const DjVuPort * port) +{ + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=a2p_map;pos;) + if (a2p_map[pos]==port) + { + GPosition this_pos=pos; + ++pos; + a2p_map.del(this_pos); + } else ++pos; +} + +GP<DjVuPort> +DjVuPortcaster::alias_to_port(const GUTF8String &alias) +{ + GCriticalSectionLock lock(&map_lock); + GPosition pos; + if (a2p_map.contains(alias, pos)) + { + DjVuPort * port=(DjVuPort *) a2p_map[pos]; + GP<DjVuPort> gp_port=is_port_alive(port); + if (gp_port) return gp_port; + else a2p_map.del(pos); + } + return 0; +} + +GPList<DjVuPort> +DjVuPortcaster::prefix_to_ports(const GUTF8String &prefix) +{ + GPList<DjVuPort> list; + { + int length=prefix.length(); + if (length) + { + GCriticalSectionLock lock(&map_lock); + for(GPosition pos=a2p_map;pos;++pos) + if (!prefix.cmp(a2p_map.key(pos), length)) + { + DjVuPort * port=(DjVuPort *) a2p_map[pos]; + GP<DjVuPort> gp_port=is_port_alive(port); + if (gp_port) list.append(gp_port); + } + } + } + return list; +} + +void +DjVuPortcaster::del_port(const DjVuPort * port) +{ + GCriticalSectionLock lock(&map_lock); + + GPosition pos; + + // Update the "aliases map" + clear_aliases(port); + + // Update "contents map" + if (cont_map.contains(port, pos)) cont_map.del(pos); + + // Update "route map" + if (route_map.contains(port, pos)) + { + delete (GList<void *> *) route_map[pos]; + route_map.del(pos); + } + for(pos=route_map;pos;) + { + GList<void *> & list=*(GList<void *> *) route_map[pos]; + GPosition list_pos; + if (list.search((void *) port, list_pos)) list.del(list_pos); + if (!list.size()) + { + delete &list; + GPosition tmp_pos=pos; + ++pos; + route_map.del(tmp_pos); + } else ++pos; + } +} + +void +DjVuPortcaster::add_route(const DjVuPort * src, DjVuPort * dst) + // Adds route src->dst +{ + GCriticalSectionLock lock(&map_lock); + if (cont_map.contains(src) && src->get_count()>0 && + cont_map.contains(dst) && dst->get_count()>0) + { + if (!route_map.contains(src)) route_map[src]=new GList<void *>(); + GList<void *> & list=*(GList<void *> *) route_map[src]; + if (!list.contains(dst)) list.append(dst); + } +} + +void +DjVuPortcaster::del_route(const DjVuPort * src, DjVuPort * dst) +// Deletes route src->dst +{ + GCriticalSectionLock lock(&map_lock); + + if (route_map.contains(src)) + { + GList<void *> & list=*(GList<void *> *) route_map[src]; + GPosition pos; + if (list.search(dst, pos)) list.del(pos); + if (!list.size()) + { + delete &list; + route_map.del(src); + } + } +} + +void +DjVuPortcaster::copy_routes(DjVuPort * dst, const DjVuPort * src) + // For every route src->x or x->src, it creates a new one: + // dst->x or x->dst respectively. It's useful when you create a copy + // of a port and you want the copy to stay connected. +{ + GCriticalSectionLock lock(&map_lock); + + if (!cont_map.contains(src) || src->get_count()<=0 || + !cont_map.contains(dst) || dst->get_count()<=0) return; + + for(GPosition pos=route_map;pos;++pos) + { + GList<void *> & list=*(GList<void *> *) route_map[pos]; + if (route_map.key(pos) == src) + for(GPosition pos=list;pos;++pos) + add_route(dst, (DjVuPort *) list[pos]); + for(GPosition pos=list;pos;++pos) + if ((DjVuPort*)(list[pos]) == src) + add_route((DjVuPort *) route_map.key(pos), dst); + } +} + +void +DjVuPortcaster::add_to_closure(GMap<const void *, void *> & set, + const DjVuPort * dst, int distance) +{ + // Assuming that the map's already locked + // GCriticalSectionLock lock(&map_lock); + set[dst]= (void*) (unsigned long) distance; + if (route_map.contains(dst)) + { + GList<void *> & list=*(GList<void *> *) route_map[dst]; + for(GPosition pos=list;pos;++pos) + { + DjVuPort * new_dst=(DjVuPort *) list[pos]; + if (!set.contains(new_dst)) + add_to_closure(set, new_dst, distance+1); + } + } +} + +void +DjVuPortcaster::compute_closure(const DjVuPort * src, GPList<DjVuPort> &list, bool sorted) +{ + GCriticalSectionLock lock(&map_lock); + GMap<const void*, void*> set; + if (route_map.contains(src)) + { + GList<void *> & list=*(GList<void *> *) route_map[src]; + for(GPosition pos=list;pos;++pos) + { + DjVuPort * dst=(DjVuPort *) list[pos]; + if (dst==src) add_to_closure(set, src, 0); + else add_to_closure(set, dst, 1); + } + } + + // Compute list + GPosition pos; + if (sorted) + { + // Sort in depth order + int max_dist=0; + for(pos=set;pos;++pos) + if (max_dist < (int)(long)set[pos]) + max_dist = (int)(long)set[pos]; + GArray<GList<const void*> > lists(0,max_dist); + for(pos=set;pos;++pos) + lists[(int)(long)set[pos]].append(set.key(pos)); + for(int dist=0;dist<=max_dist;dist++) + for(pos=lists[dist];pos;++pos) + { + GP<DjVuPort> p = is_port_alive((DjVuPort*) lists[dist][pos]); + if (p) list.append(p); + } + } + else + { + // Gather ports without order + for(pos=set;pos;++pos) + { + GP<DjVuPort> p = is_port_alive((DjVuPort*) set.key(pos)); + if (p) list.append(p); + } + } +} + +GURL +DjVuPortcaster::id_to_url(const DjVuPort * source, const GUTF8String &id) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GURL url; + for(GPosition pos=list;pos;++pos) + { + url=list[pos]->id_to_url(source, id); + if (!url.is_empty()) break; + } + return url; +} + +GP<DjVuFile> +DjVuPortcaster::id_to_file(const DjVuPort * source, const GUTF8String &id) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GP<DjVuFile> file; + for(GPosition pos=list;pos;++pos) + if ((file=list[pos]->id_to_file(source, id))) break; + return file; +} + +GP<DataPool> +DjVuPortcaster::request_data(const DjVuPort * source, const GURL & url) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + GP<DataPool> data; + for(GPosition pos=list;pos;++pos) + if ((data = list[pos]->request_data(source, url))) + break; + return data; +} + +bool +DjVuPortcaster::notify_error(const DjVuPort * source, const GUTF8String &msg) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + for(GPosition pos=list;pos;++pos) + if (list[pos]->notify_error(source, msg)) + return 1; + return 0; +} + +bool +DjVuPortcaster::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + GPList<DjVuPort> list; + compute_closure(source, list, true); + for(GPosition pos=list;pos;++pos) + if (list[pos]->notify_status(source, msg)) + return 1; + return 0; +} + +void +DjVuPortcaster::notify_redisplay(const DjVuImage * source) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_redisplay(source); +} + +void +DjVuPortcaster::notify_relayout(const DjVuImage * source) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_relayout(source); +} + +void +DjVuPortcaster::notify_chunk_done(const DjVuPort * source, const GUTF8String &name) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_chunk_done(source, name); +} + +void +DjVuPortcaster::notify_file_flags_changed(const DjVuFile * source, + long set_mask, long clr_mask) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_file_flags_changed(source, set_mask, clr_mask); +} + +void +DjVuPortcaster::notify_doc_flags_changed(const DjVuDocument * source, + long set_mask, long clr_mask) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_doc_flags_changed(source, set_mask, clr_mask); +} + +void +DjVuPortcaster::notify_decode_progress(const DjVuPort * source, float done) +{ + GPList<DjVuPort> list; + compute_closure(source, list); + for(GPosition pos=list; pos; ++pos) + list[pos]->notify_decode_progress(source, done); +} + +//**************************************************************************** +//******************************* DjVuPort *********************************** +//**************************************************************************** + +GURL +DjVuPort::id_to_url(const DjVuPort *, const GUTF8String &) { return GURL(); } + +GP<DjVuFile> +DjVuPort::id_to_file(const DjVuPort *, const GUTF8String &) { return 0; } + +GP<DataPool> +DjVuPort::request_data(const DjVuPort *, const GURL &) { return 0; } + +bool +DjVuPort::notify_error(const DjVuPort *, const GUTF8String &) { return 0; } + +bool +DjVuPort::notify_status(const DjVuPort *, const GUTF8String &) { return 0; } + +void +DjVuPort::notify_redisplay(const DjVuImage *) {} + +void +DjVuPort::notify_relayout(const DjVuImage *) {} + +void +DjVuPort::notify_chunk_done(const DjVuPort *, const GUTF8String &) {} + +void +DjVuPort::notify_file_flags_changed(const DjVuFile *, long, long) {} + +void +DjVuPort::notify_doc_flags_changed(const DjVuDocument *, long, long) {} + +void +DjVuPort::notify_decode_progress(const DjVuPort *, float) {} + +//**************************************************************************** +//*************************** DjVuSimplePort ********************************* +//**************************************************************************** + +GP<DataPool> +DjVuSimplePort::request_data(const DjVuPort * source, const GURL & url) +{ + G_TRY { + if (url.is_local_file_url()) + { +// GUTF8String fname=GOS::url_to_filename(url); +// if (GOS::basename(fname)=="-") fname="-"; + return DataPool::create(url); + } + } G_CATCH_ALL {} G_ENDCATCH; + return 0; +} + +bool +DjVuSimplePort::notify_error(const DjVuPort * source, const GUTF8String &msg) +{ + DjVuMessageLite::perror(msg); + return 1; +} + +bool +DjVuSimplePort::notify_status(const DjVuPort * source, const GUTF8String &msg) +{ + DjVuMessageLite::perror(msg); + return 1; +} + + + + + +//**************************************************************************** +//*************************** DjVuMemoryPort ********************************* +//**************************************************************************** + + + +GP<DataPool> +DjVuMemoryPort::request_data(const DjVuPort * source, const GURL & url) +{ + GCriticalSectionLock lk(&lock); + GP<DataPool> pool; + GPosition pos; + if (map.contains(url, pos)) + pool=map[pos]; + return pool; +} + +void +DjVuMemoryPort::add_data(const GURL & url, const GP<DataPool> & pool) +{ + GCriticalSectionLock lk(&lock); + map[url]=pool; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuPort.h b/kviewshell/plugins/djvu/libdjvu/DjVuPort.h new file mode 100644 index 00000000..99c165fb --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuPort.h @@ -0,0 +1,518 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuPort.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUPORT_H +#define _DJVUPORT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GThreads.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class DataPool; + +/** @name DjVuPort.h + Files #"DjVuPort.h"# and #"DjVuPort.cpp"# implement a communication + mechanism between different parties involved in decoding DjVu files. + It should be pretty clear that the creator of \Ref{DjVuDocument} and + \Ref{DjVuFile} would like to receive some information about the progress + of decoding, errors occurred, etc. It may also want to provide source data + for decoders (like it's done in the plugin where the real data is downloaded + from the net and is fed into DjVu decoders). + + Normally this functionality is implemented by means of callbacks which are + run when a given condition comes true. Unfortunately it's not quite easy + to implement this strategy in our case. The reason is that there may be + more than one "client" working with the same document, and the document + should send the information to each of the clients. This could be done by + means of callback {\em lists}, of course, but we want to achieve more + bulletproof results: we want to be sure that the client that we're about + to contact is still alive, and is not being destroyed by another thread. + Besides, we are going to call these "callbacks" from many places, from + many different classes. Maintaining multi-thread safe callback lists is + very difficult. + + Finally, we want to provide some default implementation of these + "callbacks" in the library, which should attempt to process the requests + themselves if they can, and contact the client only if they're unable to + do it (like in the case of \Ref{DjVuPort::request_data}() with local URL + where \Ref{DjVuDocument} can get the data from the hard drive itself not + disturbing the document's creator. + + Two classes implement a general communication mechanism: \Ref{DjVuPort} and + \Ref{DjVuPortcaster}. Any sender and recipient of requests should be a + subclass of \Ref{DjVuPort}. \Ref{DjVuPortcaster} maintains a map of + routes between \Ref{DjVuPort}s, which should be configured by somebody + else. Whenever a port wants to send a request, it calls the corresponding + function of \Ref{DjVuPortcaster}, and the portcaster relays the request to + all the destinations that it sees in the internal map. + + The \Ref{DjVuPortcaster} is responsible for keeping the map up to date by + getting rid of destinations that have been destroyed. Map updates are + performed from a single place and are serialized by a global monitor. + + @memo DjVu decoder communication mechanism. + @author Andrei Erofeev <[email protected]>\\ + L\'eon Bottou <[email protected]> + @version #$Id: DjVuPort.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + +class DjVuPort; +class DjVuPortcaster; +class DjVuFile; + +/** Base class for notification targets. + #DjVuPort# provides base functionality for classes willing to take part in + sending and receiving messages generated during decoding process. You + need to derive your class from #DjVuPort# if you want it to be able to + send or receive requests. In addition, for receiving requests you should + override one or more virtual function. + + {\bf Important remark} --- All ports should be allocated on the heap using + #operator new# and immediately secured using a \Ref{GP} smart pointer. + Ports which are not secured by a smart-pointer are not considered + ``alive'' and never receive notifications! */ + +class DjVuPort : public GPEnabled +{ +public: + DjVuPort(); + virtual ~DjVuPort(); + static void *operator new (size_t sz); + static void operator delete(void *addr); + + /** Use this function to get a copy of the global \Ref{DjVuPortcaster}. */ + static DjVuPortcaster *get_portcaster(void); + + /** Copy constructor. When #DjVuPort#s are copied, the portcaster + copies all incoming and outgoing routes of the original. */ + DjVuPort(const DjVuPort & port); + + /** Copy operator. Similarly to the copy constructor, the portcaster + copies all incoming and outgoing coming routes of the original. */ + DjVuPort & operator=(const DjVuPort & port); + + /** Should return 1 if the called class inherits class #class_name#. + When a destination receives a request, it can retrieve the pointer + to the source #DjVuPort#. This virtual function should be able + to help to identify the source of the request. For example, + \Ref{DjVuFile} is also derived from #DjVuPort#. In order for + the receiver to recognize the sender, the \Ref{DjVuFile} should + override this function to return #TRUE# when the #class_name# + is either #DjVuPort# or #DjVuFile# */ + virtual bool inherits(const GUTF8String &class_name) const; + + /** @name Notifications. + These virtual functions may be overridden by the subclasses + of #DjVuPort#. They are called by the \Ref{DjVuPortcaster} + when the port is alive and when there is a route between the + source of the notification and this port. */ + //@{ + + /** This request is issued to request translation of the ID, used + in an DjVu INCL chunk to a URL, which may be used to request + data associated with included file. \Ref{DjVuDocument} usually + intercepts all such requests, and the user doesn't have to + worry about the translation */ + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + + /** This request is used to get a file corresponding to the + given ID. \Ref{DjVuDocument} is supposed to intercept it + and either create a new instance of \Ref{DjVuFile} or reuse + an existing one from the cache. */ + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + + /** This request is issued when decoder needs additional data + for decoding. Both \Ref{DjVuFile} and \Ref{DjVuDocument} are + initialized with a URL, not the document data. As soon as + they need the data, they call this function, whose responsibility + is to locate the source of the data basing on the #URL# passed + and return it back in the form of the \Ref{DataPool}. If this + particular receiver is unable to fullfil the request, it should + return #0#. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** This notification is sent when an error occurs and the error message + should be shown to the user. The receiver should return #0# if it is + unable to process the request. Otherwise the receiver should return 1. */ + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /** This notification is sent to update the decoding status. The + receiver should return #0# if it is unable to process the + request. Otherwise the receiver should return 1. */ + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); + + /** This notification is sent by \Ref{DjVuImage} when it should be + redrawn. It may be used to implement progressive redisplay. + + @param source The sender of the request */ + virtual void notify_redisplay(const class DjVuImage * source); + + /** This notification is sent by \ref{DjVuImage} when its geometry + has been changed as a result of decoding. It may be used to + implement progressive redisplay. */ + virtual void notify_relayout(const class DjVuImage * source); + + /** This notification is sent when a new chunk has been decoded. */ + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + + /** This notification is sent after the \Ref{DjVuFile} flags have + been changed. This happens, for example, when: + \begin{itemize} + \item Decoding succeeded, failed or just stopped + \item All data has been received + \item All included files have been created + \end{itemize} + + @param source \Ref{DjVuFile}, which flags have been changed + @param set_mask bits, which have been set + @param clr_mask bits, which have been cleared */ + virtual void notify_file_flags_changed(const class DjVuFile * source, + long set_mask, long clr_mask); + + /** This notification is sent after the \Ref{DjVuDocument} flags have + been changed. This happens, for example, after it receives enough + data and can determine its structure (#BUNDLED#, #OLD_INDEXED#, etc.). + + @param source \Ref{DjVuDocument}, which flags have been changed + @param set_mask bits, which have been set + @param clr_mask bits, which have been cleared */ + virtual void notify_doc_flags_changed(const class DjVuDocument * source, + long set_mask, long clr_mask); + + /** This notification is sent from time to time while decoding is in + progress. The purpose is obvious: to provide a way to know how much + is done and how long the decoding will continue. Argument #done# is + a number from 0 to 1 reflecting the progress. */ + virtual void notify_decode_progress(const DjVuPort * source, float done); + + /** This is the standard types for defining what to do in case of errors. + This is only used by some of the subclasses, but it needs to be + defined here to guarantee all subclasses use the same enum types. + In general, many errors are non recoverable. Using a setting + other than ABORT may just result in even more errors. */ + enum ErrorRecoveryAction {ABORT=0,SKIP_PAGES=1,SKIP_CHUNKS=2,KEEP_ALL=3 }; + //@} +public: + class DjVuPortCorpse; +private: + static GCriticalSection * corpse_lock; + static DjVuPortCorpse * corpse_head, * corpse_tail; + static int corpse_num; +}; + +/** Simple port. + An instance of #DjVuSimplePort# is automatically created when you create a + \Ref{DjVuFile} or a \Ref{DjVuDocument} without specifying a port. This + simple port can retrieve data for local urls (i.e. urls referring to local + files) and display error messages on #stderr#. All other notifications + are ignored. */ + +class DjVuSimplePort : public DjVuPort +{ +public: + /// Returns 1 if #class_name# is #"DjVuPort"# or #"DjVuSimplePort"#. + virtual bool inherits(const GUTF8String &class_name) const; + + /** If #url# is local, it created a \Ref{DataPool}, connects it to the + file with the given name and returns. Otherwise returns #0#. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /// Displays error on #stderr#. Always returns 1. + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /// Displays status on #stderr#. Always returns 1. + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); +}; + + +/** Memory based port. + This \Ref{DjVuPort} maintains a map associating pseudo urls with data + segments. It processes the #request_data# notifications according to this + map. After initializing the port, you should add as many pairs #<url, + pool># as needed need and add a route from a \Ref{DjVuDocument} or + \Ref{DjVuFile} to this port. */ + +class DjVuMemoryPort : public DjVuPort +{ +public: + /// Returns 1 if #class_name# is #"DjVuPort"# or #"DjVuMemoryPort"# + virtual bool inherits(const GUTF8String &class_name) const; + + /** If #url# is one of those, that have been added before by means + of \Ref{add_data}() function, it will return the associated + \Ref{DataPool}. #ZERO# otherwize. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** Adds #<url, pool># pair to the internal map. From now on, if + somebody asks for data corresponding to the #url#, it will + be returning the #pool# */ + void add_data(const GURL & url, const GP<DataPool> & pool); +private: + GCriticalSection lock; + GPMap<GURL, DataPool>map; +}; + + + +/** Maintains associations between ports. + It monitors the status of all ports (have they been destructed yet?), + accepts requests and notifications from them and forwards them to + destinations according to internally maintained map of routes. + + The caller can modify the route map any way he likes (see + \Ref{add_route}(), \Ref{del_route}(), \Ref{copy_routes}(), + etc. functions). Any port can be either a sender of a message, an + intermediary receiver or a final destination. + + When a request is sent, the #DjVuPortcaster# computes the list of + destinations by consulting with the route map. Notifications are only + sent to ``alive'' ports. A port is alive if it is referenced by a valid + \Ref{GP} smartpointer. As a consequence, a port usually becomes alive + after running the constructor (since the returned pointer is then assigned + to a smartpointer) and is no longer alive when the port is destroyed + (because it would not be destroyed if a smartpointer was referencing it). + + Destination ports are sorted according to their distance from the source. + For example, if port {\bf A} is connected to ports {\bf B} and {\bf C} + directly, and port {\bf B} is connected to {\bf D}, then {\bf B} and {\bf + C} are assumed to be one hop away from {\bf A}, while {\bf D} is two hops + away from {\bf A}. + + In some cases the requests and notifications are sent to every possible + destination, and the order is not significant (like it is for + \Ref{notify_file_flags_changed}() request). Others should be sent to the closest + destinations first, and only then to the farthest, in case if they have + not been processed by the closest. The examples are \Ref{request_data}(), + \Ref{notify_error}() and \Ref{notify_status}(). + + The user is not expected to create the #DjVuPortcaster# itself. He should + use \Ref{get_portcaster}() global function instead. */ +class DjVuPortcaster +{ +public: + /** Use this function to get a copy of the global \Ref{DjVuPortcaster}. */ + static DjVuPortcaster *get_portcaster(void) + { return DjVuPort::get_portcaster(); } ; + + /** The default constructor. */ + DjVuPortcaster(void); + + virtual ~DjVuPortcaster(void); + + /** Removes the specified port from all routes. It will no longer + be able to receive or generate messages and will be considered + {\bf "dead"} by \Ref{is_port_alive}() function. */ + void del_port(const DjVuPort * port); + + /** Adds route from #src# to #dst#. Whenever a request is + sent or received by #src#, it will be forwarded to #dst# as well. + @param src The source + @param dst The destination */ + void add_route(const DjVuPort *src, DjVuPort *dst); + + /** The opposite of \Ref{add_route}(). Removes the association + between #src# and #dst# */ + void del_route(const DjVuPort *src, DjVuPort *dst); + + /** Copies all incoming and outgoing routes from #src# to + #dst#. This function should be called when a \Ref{DjVuPort} is + copied, if you want to preserve the connectivity. */ + void copy_routes(DjVuPort *dst, const DjVuPort *src); + + /** Returns a smart pointer to the port if #port# is a valid pointer + to an existing #DjVuPort#. Returns a null pointer otherwise. */ + GP<DjVuPort> is_port_alive(DjVuPort *port); + + /** Assigns one more {\em alias} for the specified \Ref{DjVuPort}. + {\em Aliases} are names, which can be used later to retrieve this + \Ref{DjVuPort}, if it still exists. Any \Ref{DjVuPort} may have + more than one {\em alias}. But every {\em alias} must correspond + to only one \Ref{DjVuPort}. Thus, if the specified alias is + already associated with another port, this association will be + removed. */ + void add_alias(const DjVuPort * port, const GUTF8String &alias); + + /** Removes all the aliases */ + static void clear_all_aliases(void); + + /** Removes all aliases associated with the given \Ref{DjVuPort}. */ + void clear_aliases(const DjVuPort * port); + + /** Returns \Ref{DjVuPort} associated with the given #alias#. If nothing + is known about name #alias#, or the port associated with it has + already been destroyed #ZERO# pointer will be returned. */ + GP<DjVuPort> alias_to_port(const GUTF8String &name); + + /** Returns a list of \Ref{DjVuPort}s with aliases starting with + #prefix#. If no \Ref{DjVuPort}s have been found, empty + list is returned. */ + GPList<DjVuPort> prefix_to_ports(const GUTF8String &prefix); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-empty \Ref{GURL}. */ + virtual GURL id_to_url(const DjVuPort * source, const GUTF8String &id); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-zero pointer to + \Ref{DjVuFile}. */ + virtual GP<DjVuFile> id_to_file(const DjVuPort * source, const GUTF8String &id); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns non-zero \Ref{DataPool}. */ + virtual GP<DataPool> request_data(const DjVuPort * source, const GURL & url); + + /** Computes destination list for #source# and calls the corresponding. + function in each of the ports from the destination starting from + the closest until one of them returns 1. */ + virtual bool notify_error(const DjVuPort * source, const GUTF8String &msg); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest until one of them returns 1. */ + virtual bool notify_status(const DjVuPort * source, const GUTF8String &msg); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_redisplay(const class DjVuImage * source); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_relayout(const class DjVuImage * source); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_chunk_done(const DjVuPort * source, const GUTF8String &name); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_file_flags_changed(const class DjVuFile * source, + long set_mask, long clr_mask); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_doc_flags_changed(const class DjVuDocument * source, + long set_mask, long clr_mask); + + /** Computes destination list for #source# and calls the corresponding + function in each of the ports from the destination list starting from + the closest. */ + virtual void notify_decode_progress(const DjVuPort * source, float done); + +private: + // We use these 'void *' to minimize template instantiations. + friend class DjVuPort; + GCriticalSection map_lock; + GMap<const void *, void *> route_map; // GMap<DjVuPort *, GList<DjVuPort *> *> + GMap<const void *, void *> cont_map; // GMap<DjVuPort *, DjVuPort *> + GMap<GUTF8String, const void *> a2p_map; // GMap<GUTF8String, DjVuPort *> + void add_to_closure(GMap<const void*, void*> & set, + const DjVuPort *dst, int distance); + void compute_closure(const DjVuPort *src, GPList<DjVuPort> &list, + bool sorted=false); +}; + + +inline bool +DjVuPort::inherits(const GUTF8String &class_name) const +{ + return (class_name == "DjVuPort"); +} + +inline bool +DjVuSimplePort::inherits(const GUTF8String &class_name) const +{ + return + (class_name == "DjVuSimplePort") || DjVuPort::inherits(class_name); +} + +inline bool +DjVuMemoryPort::inherits(const GUTF8String &class_name) const +{ + return + (class_name == "DjVuMemoryPort") || DjVuPort::inherits(class_name); +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp new file mode 100644 index 00000000..b359df41 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuText.cpp @@ -0,0 +1,971 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuText.cpp,v 1.10 2004/07/07 19:23:36 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuText.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "debug.h" +#include <ctype.h> + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +#ifdef min +#undef min +#endif +template<class TYPE> +static inline TYPE min(TYPE a,TYPE b) { return (a<b)?a:b; } + +//*************************************************************************** +//******************************** DjVuTXT ********************************** +//*************************************************************************** + +const char DjVuTXT::end_of_column = 013; // VT: Vertical Tab +const char DjVuTXT::end_of_region = 035; // GS: Group Separator +const char DjVuTXT::end_of_paragraph = 037; // US: Unit Separator +const char DjVuTXT::end_of_line = 012; // LF: Line Feed + +const int DjVuTXT::Zone::version = 1; + +DjVuTXT::Zone::Zone() + : ztype(DjVuTXT::PAGE), text_start(0), text_length(0), zone_parent(0) +{ +} + +DjVuTXT::Zone * +DjVuTXT::Zone::append_child() +{ + Zone empty; + empty.ztype = ztype; + empty.text_start = 0; + empty.text_length = 0; + empty.zone_parent=this; + children.append(empty); + return & children[children.lastpos()]; +} + +void +DjVuTXT::Zone::cleartext() +{ + text_start = 0; + text_length = 0; + for (GPosition i=children; i; ++i) + children[i].cleartext(); +} + +void +DjVuTXT::Zone::normtext(const char *instr, GUTF8String &outstr) +{ + if (text_length == 0) + { + // Descend collecting text below + text_start = outstr.length(); + for (GPosition i=children; i; ++i) + children[i].normtext(instr, outstr); + text_length = outstr.length() - text_start; + // Ignore empty zones + if (text_length == 0) + return; + } + else + { + // Collect text at this level + int new_start = outstr.length(); + outstr = outstr + GUTF8String(instr+text_start, text_length); + text_start = new_start; + // Clear textual information on lower level nodes + for (GPosition i=children; i; ++i) + children[i].cleartext(); + } + // Determine standard separator + char sep; + switch (ztype) + { + case COLUMN: + sep = end_of_column; break; + case REGION: + sep = end_of_region; break; + case PARAGRAPH: + sep = end_of_paragraph; break; + case LINE: + sep = end_of_line; break; + case WORD: + sep = ' '; break; + default: + return; + } + // Add separator if not present yet. + if (outstr[text_start+text_length-1] != sep) + { + outstr = outstr + GUTF8String(&sep, 1); + text_length += 1; + } +} + +unsigned int +DjVuTXT::Zone::memuse() const +{ + int memuse = sizeof(*this); + for (GPosition i=children; i; ++i) + memuse += children[i].memuse(); + return memuse; +} + + +#ifndef NEED_DECODER_ONLY +void +DjVuTXT::Zone::encode( + const GP<ByteStream> &gbs, const Zone * parent, const Zone * prev) const +{ + ByteStream &bs=*gbs; + // Encode type + bs.write8(ztype); + + // Modify text_start and bounding rectangle based on the context + // (whether there is a previous non-zero same-level-child or parent) + int start=text_start; + int x=rect.xmin, y=rect.ymin; + int width=rect.width(), height=rect.height(); + if (prev) + { + if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE) + { + // Encode offset from the lower left corner of the previous + // child in the coord system in that corner with x to the + // right and y down + x=x-prev->rect.xmin; + y=prev->rect.ymin-(y+height); + } else // Either COLUMN or WORD or CHARACTER + { + // Encode offset from the lower right corner of the previous + // child in the coord system in that corner with x to the + // right and y up + x=x-prev->rect.xmax; + y=y-prev->rect.ymin; + } + start-=prev->text_start+prev->text_length; + } else if (parent) + { + // Encode offset from the upper left corner of the parent + // in the coord system in that corner with x to the right and y down + x=x-parent->rect.xmin; + y=parent->rect.ymax-(y+height); + start-=parent->text_start; + } + // Encode rectangle + bs.write16(0x8000+x); + bs.write16(0x8000+y); + bs.write16(0x8000+width); + bs.write16(0x8000+height); + // Encode text info + bs.write16(0x8000+start); + bs.write24(text_length); + // Encode number of children + bs.write24(children.size()); + + const Zone * prev_child=0; + // Encode all children + for (GPosition i=children; i; ++i) + { + children[i].encode(gbs, this, prev_child); + prev_child=&children[i]; + } +} +#endif + +void +DjVuTXT::Zone::decode(const GP<ByteStream> &gbs, int maxtext, + const Zone * parent, const Zone * prev) +{ + ByteStream &bs=*gbs; + // Decode type + ztype = (ZoneType) bs.read8(); + if ( ztype<PAGE || ztype>CHARACTER ) + G_THROW( ERR_MSG("DjVuText.corrupt_text") ); + + // Decode coordinates + int x=(int) bs.read16()-0x8000; + int y=(int) bs.read16()-0x8000; + int width=(int) bs.read16()-0x8000; + int height=(int) bs.read16()-0x8000; + + // Decode text info + text_start = (int) bs.read16()-0x8000; +// int start=text_start; + text_length = bs.read24(); + if (prev) + { + if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE) + { + x=x+prev->rect.xmin; + y=prev->rect.ymin-(y+height); + } else // Either COLUMN or WORD or CHARACTER + { + x=x+prev->rect.xmax; + y=y+prev->rect.ymin; + } + text_start+=prev->text_start+prev->text_length; + } else if (parent) + { + x=x+parent->rect.xmin; + y=parent->rect.ymax-(y+height); + text_start+=parent->text_start; + } + rect=GRect(x, y, width, height); + // Get children size + int size = bs.read24(); + + // Checks + if (rect.isempty() || text_start<0 || text_start+text_length>maxtext ) + G_THROW( ERR_MSG("DjVuText.corrupt_text") ); + + // Process children + const Zone * prev_child=0; + children.empty(); + while (size-- > 0) + { + Zone *z = append_child(); + z->decode(gbs, maxtext, this, prev_child); + prev_child=z; + } +} + +void +DjVuTXT::normalize_text() +{ + GUTF8String newtextUTF8; + page_zone.normtext( (const char*)textUTF8, newtextUTF8 ); + textUTF8 = newtextUTF8; +} + +int +DjVuTXT::has_valid_zones() const +{ + if (!textUTF8) + return false; + if (page_zone.children.isempty() || page_zone.rect.isempty()) + return false; + return true; +} + + +#ifndef NEED_DECODER_ONLY +void +DjVuTXT::encode(const GP<ByteStream> &gbs) const +{ + ByteStream &bs=*gbs; + if (! textUTF8 ) + G_THROW( ERR_MSG("DjVuText.no_text") ); + // Encode text + int textsize = textUTF8.length(); + bs.write24( textsize ); + bs.writall( (void*)(const char*)textUTF8, textsize ); + // Encode zones + if (has_valid_zones()) + { + bs.write8(Zone::version); + page_zone.encode(gbs); + } +} +#endif + +void +DjVuTXT::decode(const GP<ByteStream> &gbs) +{ + ByteStream &bs=*gbs; + // Read text + textUTF8.empty(); + int textsize = bs.read24(); + char *buffer = textUTF8.getbuf(textsize); + int readsize = bs.read(buffer,textsize); + buffer[readsize] = 0; + if (readsize < textsize) + G_THROW( ERR_MSG("DjVuText.corrupt_chunk") ); + // Try reading zones + unsigned char version; + if ( bs.read( (void*) &version, 1 ) == 1) + { + if (version != Zone::version) + G_THROW( ERR_MSG("DjVuText.bad_version") "\t" + GUTF8String(version) ); + page_zone.decode(gbs, textsize); + } +} + +GP<DjVuTXT> +DjVuTXT::copy(void) const +{ + return new DjVuTXT(*this); +} + + +static inline bool +intersects_zone(GRect box, const GRect &zone) +{ + return + ((box.xmin < zone.xmin) + ?(box.xmax >= zone.xmin) + :(box.xmin <= zone.xmax)) + &&((box.ymin < zone.ymin) + ?(box.ymax >= zone.ymin) + :(box.ymin <= zone.ymax)); +} + +void +DjVuTXT::Zone::get_text_with_rect(const GRect &box, + int &string_start, int &string_end) const +{ + GPosition pos=children; + if(pos?box.contains(rect):intersects_zone(box,rect)) + { + const int text_end=text_start+text_length; + if(string_start == string_end) + { + string_start=text_start; + string_end=text_end; + }else + { + if (string_end < text_end) + string_end=text_end; + if(text_start < string_start) + string_start=text_start; + } + }else if(pos&&intersects_zone(box,rect)) + { + do + { + children[pos].get_text_with_rect(box,string_start,string_end); + } while(++pos); + } +} + +void +DjVuTXT::Zone::find_zones(GList<Zone *> &list, + const int string_start, const int string_end) const +{ + const int text_end=text_start+text_length; + if(text_start >= string_start) + { + if(text_end <= string_end) + { + list.append(const_cast<Zone *>(this)); + } + else if(text_start < string_end) + { + if (children.size()) + for (GPosition pos=children; pos; ++pos) + children[pos].find_zones(list,string_start,string_end); + else + list.append(const_cast<Zone *>(this)); + } + } + else if( text_end > string_start) + { + if (children.size()) + for (GPosition pos=children; pos; ++pos) + children[pos].find_zones(list,string_start,string_end); + else + list.append(const_cast<Zone *>(this)); + } +} + +void +DjVuTXT::Zone::get_smallest(GList<GRect> &list) const +{ + GPosition pos=children; + if(pos) + { + do { + children[pos].get_smallest(list); + } while (++pos); + } + else + { + list.append(rect); + } +} + +void +DjVuTXT::Zone::get_smallest(GList<GRect> &list, const int padding) const +{ + GPosition pos=children; + if(pos) + { + do { + children[pos].get_smallest(list,padding); + } while (++pos); + } + else if(zone_parent && zone_parent->ztype >= PARAGRAPH) + { + const GRect &xrect=zone_parent->rect; + if(xrect.height() < xrect.width()) + { + list.append(GRect(rect.xmin-padding,xrect.ymin-padding,rect.width() + +2*padding,xrect.height()+2*padding)); + } + else + { + list.append(GRect(xrect.xmin-padding,rect.ymin-padding,xrect.width() + +2*padding,rect.height()+2*padding)); + } + } + else + { + list.append(GRect(rect.xmin-padding,rect.ymin-padding,rect.width() + +2*padding,rect.height()+2*padding)); + } +} + +void +DjVuTXT::get_zones(int zone_type, const Zone *parent, + GList<Zone *> & zone_list) const + // get all the zones of type zone_type under zone node parent +{ + // search all branches under parent + const Zone *zone=parent; + for( int cur_ztype=zone->ztype; cur_ztype<zone_type; ++cur_ztype ) + { + GPosition pos; + for(pos=zone->children; pos; ++pos) + { + Zone *zcur=(Zone *)&zone->children[pos]; + if ( zcur->ztype == zone_type ) + { + GPosition zpos=zone_list; + if ( !zone_list.search(zcur,zpos) ) + zone_list.append(zcur); + } + else if ( zone->children[pos].ztype < zone_type ) + get_zones(zone_type, &zone->children[pos], zone_list); + } + } +} + +GList<GRect> +DjVuTXT::find_text_with_rect(const GRect &box, GUTF8String &text, + const int padding) const +{ + GList<GRect> retval; + int text_start=0; + int text_end=0; + page_zone.get_text_with_rect(box,text_start,text_end); + if(text_start != text_end) + { + GList<Zone *> zones; + page_zone.find_zones(zones,text_start,text_end); + GPosition pos=zones; + if(pos) + { + do + { + if(padding >= 0) + { + zones[pos]->get_smallest(retval,padding); + }else + { + zones[pos]->get_smallest(retval); + } + } while(++pos); + } + } + text=textUTF8.substr(text_start,text_end-text_start); + return retval; +} + + +GList<DjVuTXT::Zone *> +DjVuTXT::find_text_in_rect(GRect target_rect, GUTF8String &text) const + // returns a list of zones of type WORD in the nearest/selected paragraph +{ + GList<Zone *> zone_list; + GList<Zone *> lines; + + get_zones((int)PARAGRAPH, &page_zone, zone_list); + // it's possible that no paragraph structure exists for reasons that + // 1) ocr engine is not capable 2) file was modified by user. In such case, + // we can only make a rough guess, i.e., select all the lines intersected with + // target_rect + if (zone_list.isempty()) + { + get_zones((int)LINE, &page_zone, zone_list); + GPosition pos; + for(pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int h0=rect.height()/2; + if(rect.intersect(rect,target_rect) && rect.height()>h0) + lines.append(zone_list[pos]); + } + } else + { + GPosition pos, pos_sel=zone_list; + float ar=0; + for(pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int area=rect.area(); + if (rect.intersect(rect, target_rect)) + { + float ftmp=rect.area()/(float)area; + if ( !ar || ar<ftmp ) + { + ar=ftmp; + pos_sel=pos; + } + } + } + Zone *parag = 0; + if ( ar>0 ) parag=zone_list[pos_sel]; + zone_list.empty(); + if ( ar>0 ) + { + get_zones((int)LINE, parag, zone_list); + if ( !zone_list.isempty() ) + { + for(GPosition pos=zone_list; pos; ++pos) + { + GRect rect=zone_list[pos]->rect; + int h0=rect.height()/2; + if(rect.intersect(rect,target_rect) && rect.height()>h0) + lines.append(zone_list[pos]); + } + } + } + } + + zone_list.empty(); + if (!lines.isempty()) + { + int i=1, lsize=lines.size(); + + GList<Zone *> words; + for (GPosition pos=lines; pos; ++pos, ++i) + { + words.empty(); + get_zones((int)WORD, lines[pos], words); + + if ( lsize==1 ) + { + for(GPosition p=words;p;++p) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if (target_rect.contains(words[p]->rect)) + zone_list.append(words[p]); + } + } else + { + if (i==1) + { + bool start=true; + for(GPosition p=words; p; ++p) + { + if ( start ) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if (target_rect.contains(words[p]->rect)) + { + start=false; + zone_list.append(words[p]); + } + } else + zone_list.append(words[p]); + } + } else if (i==lsize) + { + bool end=true; + for(GPosition p=words.lastpos();p;--p) + { + if ( end ) + { + GRect rect=words[p]->rect; + if(rect.intersect(rect,target_rect)) + //if(target_rect.contains(words[p]->rect) ) + { + end=false; + zone_list.append(words[p]); + } + } else + zone_list.append(words[p]); + } + } + + if (i!=1 && i!=lsize ) + { + for(GPosition p=words;p;++p) + zone_list.append(words[p]); + } + } + } + } + + return zone_list; +} + +unsigned int +DjVuTXT::get_memory_usage() const +{ + return sizeof(*this) + textUTF8.length() + page_zone.memuse() - sizeof(page_zone); +} + + + +//*************************************************************************** +//******************************** DjVuText ********************************* +//*************************************************************************** + +void +DjVuText::decode(const GP<ByteStream> &gbs) +{ + GUTF8String chkid; + GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + while( iff.get_chunk(chkid) ) + { + if (chkid == "TXTa") + { + if (txt) + G_THROW( ERR_MSG("DjVuText.dupl_text") ); + txt = DjVuTXT::create(); + txt->decode(iff.get_bytestream()); + } + else if (chkid == "TXTz") + { + if (txt) + G_THROW( ERR_MSG("DjVuText.dupl_text") ); + txt = DjVuTXT::create(); + const GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream()); + txt->decode(gbsiff); + } + // Add decoding of other chunks here + iff.close_chunk(); + } +} + +void +DjVuText::encode(const GP<ByteStream> &gbs) +{ + if (txt) + { + const GP<IFFByteStream> giff=IFFByteStream::create(gbs); + IFFByteStream &iff=*giff; + iff.put_chunk("TXTz"); + { + GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50); + txt->encode(gbsiff); + } + iff.close_chunk(); + } + // Add encoding of other chunks here +} + + +GP<DjVuText> +DjVuText::copy(void) const +{ + GP<DjVuText> text= new DjVuText; + // Copy any primitives (if any) + *text=*this; + // Copy each substructure + if (txt) + text->txt = txt->copy(); + return text; +} + +static GUTF8String +indent ( int spaces) +{ + GUTF8String ret; + for( int i = 0 ; i < spaces ; i++ ) + ret += ' '; + return ret; +} + +static const char *tags[8]= +{ 0, + "HIDDENTEXT", + "PAGECOLUMN", + "REGION", + "PARAGRAPH", + "LINE", + "WORD", + "CHARACTER" }; +static const int tags_size=sizeof(tags)/sizeof(const char *); + +static GUTF8String +start_tag(const DjVuTXT::ZoneType zone) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone > 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="<"+GUTF8String(tags[zone])+">"; + break; + case DjVuTXT::WORD: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+">"; + break; + default: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+">\n"; + break; + } + } + return retval; +} + +static GUTF8String +start_tag(const DjVuTXT::ZoneType zone, const GUTF8String &attributes) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone > 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="<"+GUTF8String(tags[zone])+" "+attributes+">"; + break; + case DjVuTXT::WORD: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">"; + break; + default: + retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">\n"; + break; + } + } + return retval; +} + +static inline GUTF8String +start_tag(const int layer) +{ + return start_tag((const DjVuTXT::ZoneType)layer); +} + + +static GUTF8String +end_tag(const DjVuTXT::ZoneType zone) +{ + GUTF8String retval; + if((tags_size > (int)zone)&&((int)zone >= 0)) + { + switch (zone) + { + case DjVuTXT::CHARACTER: + retval="</"+GUTF8String(tags[zone])+">"; + break; + case DjVuTXT::WORD: + retval="</"+GUTF8String(tags[zone])+">\n"; + break; + default: + retval=indent(2*(int)zone+2)+"</"+tags[zone]+">\n"; + break; + } + } + return retval; +} + +static inline GUTF8String +end_tag(const int layer) +{ + return end_tag((const DjVuTXT::ZoneType)layer); +} + +static GUTF8String +tolayer(int &layer, const DjVuTXT::ZoneType next_layer) +{ + GUTF8String retval; + for( ;layer < (int)next_layer;layer++ ) + { + retval+=start_tag(layer); + } + while (layer > (int)next_layer ) + { + retval+=end_tag(--layer); + } + return retval; +} + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::Zone &zone, + const int WindowHeight ); + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::ZoneType zlayer, + const GList<DjVuTXT::Zone> &children, + const int WindowHeight ) +{ +// assert( txt->has_valid_zones() ); +// DEBUG_MSG( "--zonetype=" << txt->page_zone.ztype << "\n" ); + + // Beginning tags for missing layers + int layer=(int)zlayer; + // Output the next layer + for(GPosition pos=children ; pos ; ++pos ) + { + str_out.writestring(tolayer(layer,children[pos].ztype)); + writeText( str_out, + textUTF8, + children[pos], + WindowHeight ); + } + str_out.writestring(tolayer(layer,zlayer)); +} + +static void +writeText( ByteStream & str_out, + const GUTF8String &textUTF8, + const DjVuTXT::Zone &zone, + const int WindowHeight ) +{ +// DEBUG_MSG( "--zonetype=" << zone.ztype << "\n" ); + + const GUTF8String xindent(indent( 2 * zone.ztype + 2 )); + GPosition pos=zone.children; + // Build attribute string + if( ! pos ) + { + GUTF8String coords; + coords.format("coords=\"%d,%d,%d,%d\"", + zone.rect.xmin, WindowHeight - 1 - zone.rect.ymin, + zone.rect.xmax, WindowHeight - 1 - zone.rect.ymax); + const int start=zone.text_start; + const int end=textUTF8.firstEndSpace(start,zone.text_length); + str_out.writestring(start_tag(zone.ztype,coords)); + str_out.writestring(textUTF8.substr(start,end-start).toEscaped()); + str_out.writestring(end_tag(zone.ztype)); + } else + { + writeText(str_out,textUTF8,zone.ztype,zone.children,WindowHeight); + } +} + +void +DjVuTXT::writeText(ByteStream &str_out,const int height) const +{ + if(has_valid_zones()) + { + ::writeText(str_out,textUTF8,DjVuTXT::PAGE,page_zone.children,height); + }else + { + str_out.writestring(start_tag(DjVuTXT::PAGE)); + str_out.writestring(end_tag(DjVuTXT::PAGE)); + } +} + +void +DjVuText::writeText(ByteStream &str_out,const int height) const +{ + if(txt) + { + txt->writeText(str_out,height); + }else + { + str_out.writestring("<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n"); + } + +} +GUTF8String +DjVuTXT::get_xmlText(const int height) const +{ + GP<ByteStream> gbs(ByteStream::create()); + ByteStream &bs=*gbs; + writeText(bs,height); + bs.seek(0L); + return bs.getAsUTF8(); +} + +GUTF8String +DjVuText::get_xmlText(const int height) const +{ + GUTF8String retval; + if(txt) + { + retval=txt->get_xmlText(height); + }else + { + retval="<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n"; + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuText.h b/kviewshell/plugins/djvu/libdjvu/DjVuText.h new file mode 100644 index 00000000..61ee3667 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuText.h @@ -0,0 +1,281 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuText.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVUTEXT_H +#define _DJVUTEXT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + + +/** @name DjVuText.h + + Files #"DjVuText.h"# and #"DjVuText.cpp"# implement the mechanism for + text in DjVuImages. + + This file implements annotations understood by the DjVu plugins + and encoders. + + + using: contents of #TXT*# chunks. + + Contents of the #FORM:TEXT# should be passed to \Ref{DjVuText::decode}() + for parsing, which initializes \Ref{DjVuText::TXT} + and fills them with decoded data. + @memo Implements support for DjVuImage hidden text. + @author Andrei Erofeev <[email protected]> + @version + #$Id: DjVuText.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "GMapAreas.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class ByteStream; + +// -------- DJVUTXT -------- + +/** Description of the text contained in a DjVu page. This class contains the + textual data for the page. It describes the text as a hierarchy of zones + corresponding to page, column, region, paragraph, lines, words, etc... + The piece of text associated with each zone is represented by an offset + and a length describing a segment of a global UTF8 encoded string. */ + +class DjVuTXT : public GPEnabled +{ +protected: + DjVuTXT(void) {} +public: + /// Default creator + static GP<DjVuTXT> create(void) {return new DjVuTXT();} + + /** These constants are used to tell what a zone describes. + This can be useful for a copy/paste application. + The deeper we go into the hierarchy, the higher the constant. */ + enum ZoneType { PAGE=1, COLUMN=2, REGION=3, PARAGRAPH=4, + LINE=5, WORD=6, CHARACTER=7 }; + /** Data structure representing document textual components. + The text structure is represented by a hierarchy of rectangular zones. */ + class Zone + { + public: + Zone(); + /** Type of the zone. */ + enum ZoneType ztype; + /** Rectangle spanned by the zone */ + GRect rect; + /** Position of the zone text in string #textUTF8#. */ + int text_start; + /** Length of the zone text in string #textUTF8#. */ + int text_length; + /** List of children zone. */ + GList<Zone> children; + /** Appends another subzone inside this zone. The new zone is initialized + with an empty rectangle, empty text, and has the same type as this + zone. */ + Zone *append_child(); + /** Find the text_start and text_end indicated by the given box. */ + void get_text_with_rect(const GRect &box, + int &string_start,int &string_end ) const; + /** Find the zones used by the specified string and append them to the list. */ + void find_zones(GList<Zone *> &list, + const int string_start, const int string_end) const; + /** Finds the smallest rectangles and appends them to the list. */ + void get_smallest(GList<GRect> &list) const; + /** Finds the smallest rectangles and appends them to the list after + padding the smallest unit to fit width or height for the parent rectangle + and adding the number of specified pixels. */ + void get_smallest(GList<GRect> &list,const int padding) const; + /// Find out this Zone's parent. + const Zone *get_parent(void) const; + private: + friend class DjVuTXT; + const Zone *zone_parent; + void cleartext(); + void normtext(const char *instr, GUTF8String &outstr); + unsigned int memuse() const; + static const int version; + void encode(const GP<ByteStream> &bs, + const Zone * parent=0, const Zone * prev=0) const; + void decode(const GP<ByteStream> &bs, int maxtext, + const Zone * parent=0, const Zone * prev=0); + }; + /** Textual data for this page. + The content of this string is encoded using the UTF8 code. + This code corresponds to ASCII for the first 127 characters. + Columns, regions, paragraph and lines are delimited by the following + control character: + \begin{tabular}{lll} + {\bf Name} & {\bf Octal} & {\bf Ascii name} \\\hline\\ + {\tt DjVuText::end_of_column} & 013 & VT, Vertical Tab \\ + {\tt DjVuText::end_of_region} & 035 & GS, Group Separator \\ + {\tt DjVuText::end_of_paragraph} & 037 & US, Unit Separator \\ + {\tt DjVuText::end_of_line} & 012 & LF: Line Feed + \end{tabular} */ + GUTF8String textUTF8; + static const char end_of_column ; // VT: Vertical Tab + static const char end_of_region ; // GS: Group Separator + static const char end_of_paragraph ; // US: Unit Separator + static const char end_of_line ; // LF: Line Feed + /** Main zone in the document. + This zone represent the page. */ + Zone page_zone; + /** Tests whether there is a meaningful zone hierarchy. */ + int has_valid_zones() const; + /** Normalize textual data. Assuming that a zone hierarchy has been built + and represents the reading order. This function reorganizes the string + #textUTF8# by gathering the highest level text available in the zone + hierarchy. The text offsets and lengths are recomputed for all the + zones in the hierarchy. Separators are inserted where appropriate. */ + void normalize_text(); + /** Encode data for a TXT chunk. */ + void encode(const GP<ByteStream> &bs) const; + /** Decode data from a TXT chunk. */ + void decode(const GP<ByteStream> &bs); + /** Returns a copy of this object. */ + GP<DjVuTXT> copy(void) const; + /// Write XML formated text. + void writeText(ByteStream &bs,const int height) const; + /// Get XML formatted text. + GUTF8String get_xmlText(const int height) const; + /** Find the text specified by the rectangle. */ + GList<Zone*> find_text_in_rect(GRect target_rect, GUTF8String &text) const; + /** Find the text specified by the rectangle. */ + GList<GRect> find_text_with_rect(const GRect &box, GUTF8String &text, const int padding=0) const; + /** Get all zones of zone type zone_type under node parent. + zone_list contains the return value. */ + void get_zones(int zone_type, const Zone *parent, GList<Zone *> & zone_list) const; + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + unsigned int get_memory_usage() const; +}; + +inline const DjVuTXT::Zone * +DjVuTXT::Zone::get_parent(void) const +{ + return zone_parent; +} + + +class DjVuText : public GPEnabled +{ +protected: + DjVuText(void) {} +public: + /// Default creator. + static GP<DjVuText> create(void) {return new DjVuText();} + + /** Decodes a sequence of annotation chunks and merges contents of every + chunk with previously decoded information. This function + should be called right after applying \Ref{IFFByteStream::get_chunk}() + to data from #FORM:TEXT#. */ + void decode(const GP<ByteStream> &bs); + + /** Encodes all annotations back into a sequence of chunks to be put + inside a #FORM:TEXT#. */ + void encode(const GP<ByteStream> &bs); + + /// Returns a copy of this object + GP<DjVuText> copy(void) const; + + /** Returns the number of bytes needed by this data structure. It's + used by caching routines to estimate the size of a \Ref{DjVuImage}. */ + inline unsigned int get_memory_usage() const; + + /// Write XML formated text. + void writeText(ByteStream &bs,const int height) const; + + /// Get XML formatted text. + GUTF8String get_xmlText(const int height) const; + + GP<DjVuTXT> txt; +private: // dummy stuff + static void decode(ByteStream *); + static void encode(ByteStream *); +}; + +//@} + +inline unsigned int +DjVuText::get_memory_usage() const +{ + return (txt)?(txt->get_memory_usage()):0; +} + + +// ----- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp new file mode 100644 index 00000000..beaa01bc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp @@ -0,0 +1,2582 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002-2003 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuToPS.cpp,v 1.23 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuToPS.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "DjVuImage.h" +#include "DjVuText.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "JB2Image.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "debug.h" +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <math.h> +#ifdef UNIX +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const size_t ps_string_size=15000; + +// *************************************************************************** +// ****************************** Options ************************************ +// *************************************************************************** + +DjVuToPS::Options:: +Options(void) +: format(PS), + level(2), + orientation(AUTO), + mode(COLOR), + zoom(0), + color(true), + calibrate(true), + text(false), + gamma((double)2.2), + copies(1), + frame(false), + cropmarks(false), + bookletmode(OFF), + bookletmax(0), + bookletalign(0), + bookletfold(18), + bookletxfold(200) +{} + +void +DjVuToPS::Options:: +set_format(Format xformat) +{ + if (xformat != EPS && xformat != PS) + G_THROW(ERR_MSG("DjVuToPS.bad_format")); + format=xformat; +} + +void +DjVuToPS::Options:: +set_level(int xlevel) +{ + if (xlevel<1 || xlevel>3) + G_THROW(ERR_MSG("DjVuToPS.bad_level") + + GUTF8String("\t") + GUTF8String(xlevel)); + level=xlevel; +} + +void +DjVuToPS::Options:: +set_orientation(Orientation xorientation) +{ + if (xorientation!=PORTRAIT && + xorientation!=LANDSCAPE && + xorientation!=AUTO ) + G_THROW(ERR_MSG("DjVuToPS.bad_orient")); + orientation=xorientation; +} + +void +DjVuToPS::Options:: +set_mode(Mode xmode) +{ + if (xmode!=COLOR && xmode!=FORE && xmode!=BACK && xmode!=BW) + G_THROW(ERR_MSG("DjVuToPS.bad_mode")); + mode=xmode; +} + +void +DjVuToPS::Options:: +set_zoom(int xzoom) +{ + if (xzoom!=0 && !(xzoom>=5 && xzoom<=999)) + G_THROW(ERR_MSG("DjVuToPS.bad_zoom")); + zoom=xzoom; +} + +void +DjVuToPS::Options:: +set_color(bool xcolor) +{ + color=xcolor; +} + +void +DjVuToPS::Options:: +set_sRGB(bool xcalibrate) +{ + calibrate=xcalibrate; +} + +void +DjVuToPS::Options:: +set_gamma(double xgamma) +{ + if (xgamma<(double)(0.3-0.0001) || xgamma>(double)(5.0+0.0001)) + G_THROW(ERR_MSG("DjVuToPS.bad_gamma")); + gamma=xgamma; +} + +void +DjVuToPS::Options:: +set_copies(int xcopies) +{ + if (xcopies<=0) + G_THROW(ERR_MSG("DjVuToPS.bad_number")); + copies=xcopies; +} + +void +DjVuToPS::Options:: +set_frame(bool xframe) +{ + frame=xframe; +} + +void +DjVuToPS::Options:: +set_cropmarks(bool xmarks) +{ + cropmarks=xmarks; +} + +void +DjVuToPS::Options:: +set_text(bool xtext) +{ + text=xtext; +} + +void +DjVuToPS::Options:: +set_bookletmode(BookletMode m) +{ + bookletmode = m; +} + +void +DjVuToPS::Options:: +set_bookletmax(int m) +{ + bookletmax = 0; + if (m > 0) + bookletmax = (m+3)/4; + bookletmax *= 4; +} + +void +DjVuToPS::Options:: +set_bookletalign(int m) +{ + bookletalign = m; +} + +void +DjVuToPS::Options:: +set_bookletfold(int fold, int xfold) +{ + if (fold >= 0) + bookletfold = fold; + if (xfold >= 0) + bookletxfold = xfold; +} + + +// *************************************************************************** +// ******************************* DjVuToPS ********************************** +// *************************************************************************** + +static char bin2hex[256][2]; + +DjVuToPS::DjVuToPS(void) +{ + DEBUG_MSG("DjVuToPS::DjVuToPS(): initializing...\n"); + DEBUG_MAKE_INDENT(3); + DEBUG_MSG("Initializing dig2hex[]\n"); + // Creating tables for bin=>text translation + static char * dig2hex="0123456789ABCDEF"; + int i; + for(i=0;i<256;i++) + { + bin2hex[i][0]=dig2hex[i/16]; + bin2hex[i][1]=dig2hex[i%16]; + } + refresh_cb=0; + refresh_cl_data=0; + prn_progress_cb=0; + prn_progress_cl_data=0; + dec_progress_cb=0; + dec_progress_cl_data=0; + info_cb=0; + info_cl_data=0; +} + +#ifdef __GNUC__ +static void +write(ByteStream &str, const char *format, ...) +__attribute__((format (printf, 2, 3))); +#endif + +static void +write(ByteStream &str, const char *format, ...) +{ + /* Will output the formated string to the specified \Ref{ByteStream} + like #fprintf# would do it for a #FILE#. */ + va_list args; + va_start(args, format); + GUTF8String tmp; + tmp.vformat(format, args); + str.writall((const char *) tmp, tmp.length()); +} + +// ************************* DOCUMENT LEVEL ********************************* + +void +DjVuToPS:: +store_doc_prolog(ByteStream &str, int pages, int dpi, GRect *grect) +{ + /* Will store the {\em document prolog}, which is basically a + block of document-level comments in PS DSC 3.0 format. + @param str Stream where PostScript data should be written + @param pages Total number of pages + @param dpi (EPS mode only) + @param grect (EPS mode only) */ + DEBUG_MSG("storing the document prolog\n"); + DEBUG_MAKE_INDENT(3); + if (options.get_format()==Options::EPS) + write(str, + "%%!PS-Adobe-3.0 EPSF 3.0\n" + "%%%%BoundingBox: 0 0 %d %d\n", + (grect->width()*100+dpi-1)/dpi, + (grect->height()*100+dpi-1)/dpi ); + else + write(str, "%%!PS-Adobe-3.0\n"); + write(str, + "%%%%Title: DjVu PostScript document\n" + "%%%%Copyright: Copyright (c) 1998-1999 AT&T\n" + "%%%%Creator: DjVu (code by Andrei Erofeev)\n" + "%%%%DocumentData: Clean7Bit\n"); + // Date + time_t tm=time(0); + write(str, "%%%%CreationDate: %s", ctime(&tm)); + // For +#ifdef UNIX + passwd *pswd = getpwuid(getuid()); + if (pswd) + { + char *s = strchr(pswd->pw_gecos, ','); + if (s) + *s = 0; + s = 0; + if (pswd->pw_gecos && strlen(pswd->pw_gecos)) + s = pswd->pw_gecos; + else if (pswd->pw_name && strlen(pswd->pw_name)) + s = pswd->pw_name; + if (s) + write(str, "%%%%For: %s\n", s); + } +#endif + // Language + write(str, "%%%%LanguageLevel: %d\n", options.get_level()); + if (options.get_level()<2 && options.get_color()) + write(str, "%%%%Extensions: CMYK\n"); + // Pages + write(str, "%%%%Pages: %d\n",pages ); + write(str, "%%%%PageOrder: Ascend\n"); + // Orientation + if (options.get_orientation() != Options::AUTO) + write(str, "%%%%Orientation: %s\n", + options.get_orientation()==Options::PORTRAIT ? + "Portrait" : "Landscape" ); + // Requirements + if (options.get_format() == Options::PS) + { + write(str, "%%%%Requirements:"); + if (options.get_color()) + write(str, " color"); + if (options.get_copies()>1) + write(str, " numcopies(%d)", options.get_copies()); + if (options.get_level()>=2) + { + if (options.get_copies()>1) + write(str, " collate"); + if (options.get_bookletmode() == Options::RECTOVERSO) + write(str, " duplex(tumble)"); + } + write(str, "\n"); + } + // End + write(str, + "%%%%EndComments\n" + "%%%%EndProlog\n" + "\n"); +} + +void +DjVuToPS:: +store_doc_setup(ByteStream &str) +{ + /* Will store the {\em document setup}, which is a set of + PostScript commands and functions used to inspect and prepare + the PostScript interpreter environment before displaying images. */ + write(str, + "%%%%BeginSetup\n" + "/doc-origstate save def\n"); + if (options.get_level()>=2) + { + if (options.get_format() == Options::PS) + { + if (options.get_copies()>1) + write(str, + "[{\n" + "%%%%BeginFeature: NumCopies %d\n" + "<< /NumCopies %d >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n" + "[{\n" + "%%%%BeginFeature: Collate\n" + "<< /Collate true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n", + options.get_copies(), + options.get_copies() ); + if (options.get_bookletmode()==Options::RECTOVERSO) + write(str, + "[{\n" + "%%%%BeginFeature: Duplex DuplexTumble\n" + "<< /Duplex true /Tumble true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n"); + } + if (options.get_color()) + write(str, + "%% -- procs for reading color image\n" + "/readR () def\n" + "/readG () def\n" + "/readB () def\n" + "/ReadData {\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferR readstring pop /readR exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferG readstring pop /readG exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferB readstring pop /readB exch def\n" + " dup status { flushfile } { pop } ifelse\n" + "} bind def\n" + "/ReadR {\n" + " readR length 0 eq { ReadData } if\n" + " readR /readR () def\n" + "} bind def\n" + "/ReadG {\n" + " readG length 0 eq { ReadData } if\n" + " readG /readG () def\n" + "} bind def\n" + "/ReadB {\n" + " readB length 0 eq { ReadData } if\n" + " readB /readB () def\n" + "} bind def\n"); + write(str, + "%% -- procs for foreground layer\n" + "/g {gsave 0 0 0 0 5 index 5 index setcachedevice\n" + " true [1 0 0 1 0 0] 5 4 roll imagemask grestore\n" + "} bind def\n" + "/gn {gsave 0 0 0 0 6 index 6 index setcachedevice\n" + " true [1 0 0 1 0 0] 3 2 roll 5 1 roll \n" + " { 1 sub 0 index 2 add 1 index 1 add roll\n" + " } imagemask grestore pop \n" + "} bind def\n" + "/c {setcolor rmoveto glyphshow} bind def\n" + "/s {rmoveto glyphshow} bind def\n" + "/S {rmoveto gsave show grestore} bind def\n" + "/F {(Helvetica) findfont exch scalefont setfont} bind def\n" + "%% -- emulations\n" + "systemdict /rectstroke known not {\n" + " /rectstroke %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath stroke\n" + " } bind def } if\n" + "systemdict /rectclip known not {\n" + " /rectclip %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath clip\n" + " } bind def } if\n" + "%% -- color space\n" ); + if (options.get_sRGB()) + write(str, + "/DjVuColorSpace [ %s\n" + "<< /DecodeLMN [ { dup 0.03928 le {\n" + " 12.92321 div\n" + " } {\n" + " 0.055 add 1.055 div 2.4 exp\n" + " } ifelse } bind dup dup ]\n" + " /MatrixLMN [\n" + " 0.412457 0.212673 0.019334\n" + " 0.357576 0.715152 0.119192\n" + " 0.180437 0.072175 0.950301 ]\n" + " /WhitePoint [ 0.9505 1 1.0890 ] %% D65 \n" + " /BlackPoint[0 0 0] >> ] def\n", + (options.get_color()) ? "/CIEBasedABC" : "/CIEBasedA" ); + else if (options.get_color()) + write(str,"/DjVuColorSpace /DeviceRGB def\n"); + else + write(str,"/DjVuColorSpace /DeviceGray def\n"); + } + else + { + // level<2 + if (options.get_format() == Options::PS) + if (options.get_copies() > 1) + write(str,"/#copies %d def\n", options.get_copies()); + if (options.get_color()) + write(str, + "%% -- buffers for reading image\n" + "/buffer8 () def\n" + "/buffer24 () def\n" + "%% -- colorimage emulation\n" + "systemdict /colorimage known {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " } bind def\n" + " /ColorImage {\n" + " colorimage\n" + " } bind def\n" + "} {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " /data exch def /datalen data length def\n" + " /cnt 0 def\n" + " 0 1 datalen 3 idiv 1 sub {\n" + " buffer8 exch\n" + " data cnt get 20 mul /cnt cnt 1 add def\n" + " data cnt get 32 mul /cnt cnt 1 add def\n" + " data cnt get 12 mul /cnt cnt 1 add def\n" + " add add 64 idiv put\n" + " } for\n" + " buffer8 0 datalen 3 idiv getinterval\n" + " } bind def\n" + " /ColorImage {\n" + " pop pop image\n" + " } bind def\n" + "} ifelse\n"); + } // level<2 + write(str, "%%%%EndSetup\n\n"); +} + +void +DjVuToPS:: +store_doc_trailer(ByteStream &str) +{ + /* Will store the {\em document trailer}, which is a clean-up code + used to return the PostScript interpeter back to the state, in which + it was before displaying this document. */ + write(str, + "%%%%Trailer\n" + "doc-origstate restore\n" + "%%%%EOF\n"); +} + +// *********************************************************************** +// ***************************** PAGE LEVEL ****************************** +// *********************************************************************** + +static unsigned char * +ASCII85_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), encode it using {\bf ASCII85} algorithm, and + output the result into the destination buffer pointed by #dst#. The + function returns pointer to the first unused byte in the destination + buffer. */ + int symbols=0; + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr+=4) + { + unsigned int num=0; + if (ptr+3<src_end) + { + num |= ptr[0] << 24; + num |= ptr[1] << 16; + num |= ptr[2] << 8; + num |= ptr[3]; + } + else + { + num |= ptr[0] << 24; + if (ptr+1<src_end) + num |= ptr[1] << 16; + if (ptr+2<src_end) + num |= ptr[2] << 8; + } + int a1, a2, a3, a4, a5; + a5=num % 85; num/=85; + a4=num % 85; num/=85; + a3=num % 85; num/=85; + a2=num % 85; + a1=num / 85; + *dst++ = a1+33; + *dst++ = a2+33; + if (ptr+1<src_end) + *dst++ = a3+33; + if (ptr+2<src_end) + *dst++ = a4+33; + if (ptr+3<src_end) + *dst++ = a5+33; + symbols += 5; + if (symbols > 70 && ptr+4<src_end) + { + *dst++='\n'; + symbols=0; + } + } + return dst; +} + +static unsigned char * +RLE_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), RLE encode it, and output the result into the + destination buffer pointed by #dst#. #counter# is used to count the + number of output bytes. The function returns pointer to the first unused + byte in the destination buffer. */ + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr++) + { + if (ptr==src_end-1) + { + *dst++=0; *dst++=*ptr; + } + else if (ptr[0]!=ptr[1]) + { + // Guess how many non repeating bytes we have + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]==ptr1[1] || ptr1-ptr>=128) break; + int pixels=ptr1-ptr; + *dst++=pixels-1; + for(int cnt=0;cnt<pixels;cnt++) + *dst++=*ptr++; + ptr--; + } + else + { + // Get the number of repeating bytes + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]!=ptr1[1] || ptr1-ptr+1>=128) break; + int pixels=ptr1-ptr+1; + *dst++=257-pixels; + *dst++=*ptr; + ptr=ptr1; + } + } + return dst; +} + +#define GRAY(r,g,b) (((r)*20+(g)*32+(b)*12)/64) + +void +DjVuToPS:: +store_page_setup(ByteStream &str, + int dpi, + const GRect &grect, + int align ) +{ + /* Will store PostScript code necessary to prepare page for + the coming \Ref{DjVuImage}. This is basically a scaling + code plus initialization of some buffers. */ + if (options.get_format() == Options::EPS) + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/image-dpi %d def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/coeff 100 image-dpi div def\n" + "/a11 coeff def\n" + "/a12 0 def\n" + "/a13 0 def\n" + "/a21 0 def\n" + "/a22 coeff def\n" + "/a23 0 def\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin printing\n", + dpi, grect.width(), grect.height() ); + else + { + int margin = 0; + const char *xauto = "false"; + const char *xportrait = "false"; + const char *xfit = "false"; + if (options.get_orientation()==Options::AUTO) + xauto = "true"; + if (options.get_orientation()==Options::PORTRAIT) + xportrait = "true"; + if (options.get_zoom()<=0) + xfit = "true"; + if (options.get_cropmarks()) + margin = 36; + else if (options.get_frame()) + margin = 6; + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/auto-orient %s def\n" + "/portrait %s def\n" + "/fit-page %s def\n" + "/zoom %d def\n" + "/image-dpi %d def\n" + "clippath pathbbox newpath\n" + "2 index sub exch 3 index sub\n" + "/page-width exch def\n" + "/page-height exch def\n" + "/page-y exch def\n" + "/page-x exch def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/margin %d def\n" + "/halign %d def\n" + "/valign 0 def\n", + xauto, xportrait, xfit, options.get_zoom(), + dpi, grect.width(), grect.height(), + margin, align ); + write(str, + "%% -- position page\n" + "auto-orient {\n" + " image-height image-width sub\n" + " page-height page-width sub\n" + " mul 0 ge /portrait exch def\n" + "} if\n" + "fit-page {\n" + " /page-width page-width margin sub\n" + " halign 0 eq { margin sub } if def\n" + " /page-height page-height margin sub\n" + " valign 0 eq { margin sub } if def\n" + " /page-x page-x halign 0 ge { margin add } if def\n" + " /page-y page-y valign 0 ge { margin add } if def\n" + "} if\n" + "portrait {\n" + " fit-page {\n" + " image-height page-height div\n" + " image-width page-width div\n" + " gt {\n" + " page-height image-height div /coeff exch def\n" + " } {\n" + " page-width image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /start-y page-y page-height image-height\n" + " coeff mul sub 2 div valign 1 add mul add def\n" + " /a11 coeff def\n" + " /a12 0 def\n" + " /a13 start-x def\n" + " /a21 0 def\n" + " /a22 coeff def\n" + " /a23 start-y def\n" + "} { %% landscape\n" + " fit-page {\n" + " image-height page-width div\n" + " image-width page-height div\n" + " gt {\n" + " page-width image-height div /coeff exch def\n" + " } {\n" + " page-height image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width add page-width image-height\n" + " coeff mul sub 2 div valign 1 add mul sub def\n" + " /start-y page-y page-height image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /a11 0 def\n" + " /a12 coeff neg def\n" + " /a13 start-x image-y coeff neg mul sub def\n" + " /a21 coeff def\n" + " /a22 0 def\n" + " /a23 start-y image-x coeff mul add def \n" + "} ifelse\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin print\n"); + } +} + +void +DjVuToPS:: +store_page_trailer(ByteStream &str) +{ + write(str, + "%% -- end print\n" + "grestore\n"); + if (options.get_frame()) + write(str, + "%% Drawing frame\n" + "gsave 0.7 setgray 0.5 coeff div setlinewidth 0 0\n" + "image-width image-height rectstroke\n" + "grestore\n"); + if (options.get_cropmarks() && + options.get_format() != Options::EPS ) + write(str, + "%% Drawing crop marks\n" + "/cm { gsave translate rotate 1 coeff div dup scale\n" + " 0 setgray 0.5 setlinewidth -36 0 moveto 0 0 lineto\n" + " 0 -36 lineto stroke grestore } bind def\n" + "0 0 0 cm 180 image-width image-height cm\n" + "90 image-width 0 cm 270 0 image-height cm\n"); + write(str, + "page-origstate restore\n"); +} + +static int +compute_red(int w, int h, int rw, int rh) +{ + for (int red=1; red<16; red++) + if (((w+red-1)/red==rw) && ((h+red-1)/red==rh)) + return red; + return 16; +} + +static int +get_bg_red(GP<DjVuImage> dimg) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + if (width<=0 || height<=0) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + return 0; +} + +static GP<GPixmap> +get_bg_pixmap(GP<DjVuImage> dimg, const GRect &rect) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + GP<DjVuInfo> info = dimg->get_info(); + if (width<=0 || height<=0 || !info) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm = bg44->get_pixmap(1,rect); + return pm; + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm->init(*bgpm, rect); + return pm; + } + // FAILURE + return 0; +} + +void +DjVuToPS:: +make_gamma_ramp(GP<DjVuImage> dimg) +{ + double targetgamma = options.get_gamma(); + double whitepoint = (options.get_sRGB() ? 255 : 280); + for (int i=0; i<256; i++) + ramp[i] = i; + if (! dimg->get_info()) + return; + if (targetgamma < 0.1) + return; + double filegamma = dimg->get_info()->gamma; + double correction = filegamma / targetgamma; + if (correction<0.1 || correction>10) + return; + { + for (int i=0; i<256; i++) + { + double x = (double)(i)/255.0; + if (correction != 1.0) + x = pow(x, correction); + int j = (int) floor(whitepoint * x + 0.5); + ramp[i] = (j>255) ? 255 : (j<0) ? 0 : j; + } + } +} + +void +DjVuToPS:: +print_fg_2layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + unsigned char *blit_list) +{ + // Pure-jb2 or color-jb2 case. + GPixel p; + int currentx=0; + int currenty=0; + GP<DjVuPalette> pal = dimg->get_fgbc(); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int current_blit; + for(current_blit=0; current_blit<num_blits; current_blit++) + { + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + if ((pal) && !(options.get_mode()==Options::BW)) + { + pal->index_to_color(pal->colordata[current_blit], p); + if (options.get_color()) + { + write(str,"/%d %d %d %f %f %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[p.r]/255.0, ramp[p.g]/255.0, ramp[p.b]/255.0); + } + else + { + write(str,"/%d %d %d %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[GRAY(p.r, p.g, p.b)]/255.0); + } + } + else + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + } + currentx = blit->left; + currenty = blit->bottom; + } + } +} + +void +DjVuToPS:: +print_fg_3layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect, + unsigned char *blit_list ) +{ + GRect prn_rect; + GP<GPixmap> brush = dimg->get_fgpm(); + if (! brush) return; + int br = brush->rows(); + int bc = brush->columns(); + int red = compute_red(dimg->get_width(),dimg->get_height(),bc,br); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + int color_nb = ((options.get_color()) ? 3 : 1); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int pw = bc; + int ph = 2; + + write(str, + "/P {\n" + " 11 dict dup begin 4 1 roll\n" + " /PatternType 1 def\n" + " /PaintType 1 def\n" + " /TilingType 1 def\n" + " /H exch def\n" + " /W exch def\n" + " /Red %d def\n" + " /PatternString exch def\n" + " /XStep W Red mul def\n" + " /YStep H Red mul def\n" + " /BBox [0 0 XStep YStep] def\n" + " /PaintProc { begin\n" + " Red dup scale\n" + " << /ImageType 1 /Width W /Height H\n" + " /BitsPerComponent 8 /Interpolate false\n" + " /Decode [%s] /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource PatternString >> image\n" + " end } bind def\n" + " 0 0 XStep YStep rectclip\n" + " end matrix makepattern\n" + " /Pattern setcolorspace setpattern\n" + " 0 0 moveto\n" + "} def\n", red, (color_nb == 1) ? "0 1" : "0 1 0 1 0 1" ); + + unsigned char *s; + GPBuffer<unsigned char> gs(s,pw*ph*color_nb); + unsigned char *s_ascii_encoded; + GPBuffer<unsigned char> gs_ascii_encoded(s_ascii_encoded,pw*ph*2*color_nb); + { + for (int y=prn_rect.ymin; y<prn_rect.ymax; y+=ph) + for (int x=prn_rect.xmin; x<prn_rect.xmax; x+=pw) + { + int w = ((x+pw > prn_rect.xmax) ? prn_rect.xmax-x : pw); + int h = ((y+ph > prn_rect.ymax) ? prn_rect.ymax-y : ph); + int currentx = x * red; + int currenty = y * red; + // Find first intersecting blit + int current_blit; + int num_blits = jb2->get_blit_count(); + GRect rect1(currentx,currenty, w*red, h*red); + for(current_blit=0; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + break; + } + if (current_blit >= num_blits) + continue; + // Setup pattern + write(str,"gsave %d %d translate\n", currentx, currenty); + write(str,"<~"); + unsigned char *q = s; + for(int current_row = y; current_row<y+h; current_row++) + { + GPixel *row_pix = (*brush)[current_row]; + for(int current_col = x; current_col<x+w; current_col++) + { + GPixel &p = row_pix[current_col]; + if (color_nb>1) + { + *q++ = ramp[p.r]; + *q++ = ramp[p.g]; + *q++ = ramp[p.b]; + } + else + { + *q++ = ramp[GRAY(p.r,p.g,p.b)]; + } + } + } + unsigned char *stop_ascii = + ASCII85_encode(s_ascii_encoded,s,s+w*h*color_nb); + *stop_ascii++='\0'; + write(str,"%s",s_ascii_encoded); + write(str,"~> %d %d P\n", w, h); + // Keep performing blits + for(; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + currentx = blit->left; + currenty = blit->bottom; + } + } + write(str,"grestore\n"); + } + // Cleanup + } +} + +void +DjVuToPS:: +print_fg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect ) +{ + GP<JB2Image> jb2=dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int num_shapes = jb2->get_shape_count(); + unsigned char *dict_shapes = 0; + unsigned char *blit_list = 0; + GPBuffer<unsigned char> gdict_shapes(dict_shapes,num_shapes); + GPBuffer<unsigned char> gblit_list(blit_list,num_blits); + for(int i=0; i<num_shapes; i++) + { + dict_shapes[i]=0; + } + for(int current_blit=0; current_blit<num_blits; current_blit++) + { + JB2Blit *blit = jb2->get_blit(current_blit); + JB2Shape *shape = & jb2->get_shape(blit->shapeno); + blit_list[current_blit] = 0; + if (! shape->bits) + continue; + GRect rect2(blit->left, blit->bottom, + shape->bits->columns(), shape->bits->rows()); + if (rect2.intersect(rect2, prn_rect)) + { + dict_shapes[blit->shapeno] = 1; + blit_list[current_blit] = 1; + } + } + write(str, + "%% --- now doing the foreground\n" + "gsave DjVuColorSpace setcolorspace\n" ); + // Define font + write(str, + "/$DjVuLocalFont 7 dict def\n" + "$DjVuLocalFont begin\n" + "/FontType 3 def \n" + "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontBBox [0 0 1 .5] def\n" + "/CharStrings %d dict def\n" + "/Encoding 2 array def\n" + "0 1 1 {Encoding exch /.notdef put} for \n" + "CharStrings begin\n" + "/.notdef {} def\n", + num_shapes+1); + for(int current_shape=0; current_shape<num_shapes; current_shape++) + { + if (dict_shapes[current_shape]) + { + JB2Shape *shape = & jb2->get_shape(current_shape); + GP<GBitmap> bitmap = shape->bits; + int rows = bitmap->rows(); + int columns = bitmap->columns(); + int nbytes = (columns+7)/8*rows+1; + int nrows = rows; + int nstrings=0; + if (nbytes>(int)ps_string_size) //max string length + { + nrows=ps_string_size/((columns+7)/8); + nbytes=(columns+7)/8*nrows+1; + } + unsigned char *s_start; + GPBuffer<unsigned char> gs_start(s_start,nbytes); + unsigned char *s_ascii; + GPBuffer<unsigned char> gs_ascii(s_ascii,nbytes*2); + write(str,"/%d {",current_shape); + + unsigned char *s = s_start; + for(int current_row=0; current_row<rows; current_row++) + { + unsigned char * row_bits = (*bitmap)[current_row]; + unsigned char acc = 0; + unsigned char mask = 0; + for(int current_col=0; current_col<columns; current_col++) + { + if (mask == 0) + mask = 0x80; + if (row_bits[current_col]) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + *s=acc; + s++; + acc = mask = 0; + } + } + if (mask != 0) + { + *s=acc; + s++; + } + if (!((current_row+1)%nrows)) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + s=s_start; + nstrings++; + } + } + if (s!=s_start) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + nstrings++; + } + if (nstrings==1) + write(str," %d %d g} def\n", columns, rows); + else + write(str," %d %d %d gn} def\n", columns, rows,nstrings); + } + } + write(str, + "end\n" + "/BuildGlyph {\n" + " exch /CharStrings get exch\n" + " 2 copy known not\n" + " {pop /.notdef} if\n" + " get exec \n" + "} bind def\n" + "end\n" + "/LocalDjVuFont $DjVuLocalFont definefont pop\n" + "/LocalDjVuFont findfont setfont\n" ); + write(str, + "-%d -%d translate\n" + "0 0 moveto\n", + prn_rect.xmin, prn_rect.ymin); + // Print the foreground layer + if (dimg->get_fgpm() && !(options.get_mode()==Options::BW)) + print_fg_3layer(str, dimg, prn_rect, blit_list); + else + print_fg_2layer(str, dimg, prn_rect, blit_list); + write(str, "/LocalDjVuFont undefinefont grestore\n"); +} + + +void +DjVuToPS:: +print_bg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect) +{ + GP<GPixmap> pm; + GRect prn_rect; + double print_done = 0; + int red = 0; + write(str, "%% --- now doing the background\n"); + if (! (red = get_bg_red(dimg))) + return; + write(str, + "gsave -%d -%d translate\n" + "/bgred %d def bgred bgred scale\n", + cprn_rect.xmin % red, + cprn_rect.ymin % red, + red); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + // Display image + int band_bytes = 125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*23/10; + bool do_color = options.get_color(); + if (!dimg->is_legal_photo() && + !dimg->is_legal_compound() || + options.get_mode()==Options::BW) + do_color = false; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + else + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + GP<GPixmap> pm = 0; + // Compute next band + grectBand.ymin=grectBand.ymax; + grectBand.ymax=grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax>prn_rect.ymax) + grectBand.ymax=prn_rect.ymax; + pm = get_bg_pixmap(dimg, grectBand); + unsigned char *buf_ptr = buffer; + if (pm) + { + if (do_color) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to + = rle_out+(rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + } + } // if (pm) + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double)(grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } // while(grectBand.yax<grect.ymax) + if (! do_color) + { + unsigned char * buf_ptr = buffer; + *rle_out_end++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++='~'; + *buf_ptr++='>'; + *buf_ptr++='\n'; + str.writall(buffer, buf_ptr-buffer); + } + } + //restore the scaling + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev1(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GP<GBitmap> bm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + bm = dimg->get_bitmap(test,all); + if (! pm && ! bm) + return; + write(str, + "%% --- now doing a level 1 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + buffer_size = buffer_size*21/10; + bool do_color = false; + bool do_color_or_gray = false; + if (pm && (options.get_mode() != Options::BW)) + do_color_or_gray = true; + if (do_color_or_gray && options.get_color()) + do_color = true; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, "/buffer24 %d string def\n", 3*prn_rect.width()); + if (do_color_or_gray) + write(str, "/buffer8 %d string def\n", prn_rect.width()); + else + write(str, "/buffer8 %d string def\n", (prn_rect.width()+7)/8); + if (do_color) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ ColorProc } false 3 ColorImage\n", + prn_rect.width(), prn_rect.height()); + } + else if (do_color_or_gray) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "%d %d 1 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char * buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + { + // Start storing image in bands + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + bm = 0; + if (do_color_or_gray) + { + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + } + else + { + bm = dimg->get_bitmap(grectBand, all); + } + // Store next band + unsigned char *buf_ptr = buffer; + int symbols=0; + for (int y=0; y<grectBand.height(); y++) + { + if (pm && do_color_or_gray) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (do_color) + { + char *data; + data = bin2hex[ramp[pix->r]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->g]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->b]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 6; + } + else + { + char *data; + data = bin2hex[ramp[GRAY(pix->r,pix->g,pix->b)]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols=0; + } + } + } + else if (bm) + { + unsigned char *pix = (*bm)[y]; + unsigned char acc = 0; + unsigned char mask = 0; + char *data; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (mask == 0) + mask = 0x80; + if (! *pix) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + data = bin2hex[acc]; + acc = 0; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols = 0; + } + } + } + if (mask != 0) + { + data = bin2hex[acc]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + } + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev2(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + return; + write(str, + "%% --- now doing a level 2 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*21/10 + 32; + bool do_color = options.get_color(); + if (do_color) + { + buffer_size *= 3; + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + // Store next band + unsigned char *buf_ptr = buffer; + if (do_color && pm) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else if (pm) + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to = rle_out + + (rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + if (grectBand.ymax >= prn_rect.ymax) + { + *rle_out_end++ = 0x80; // Add EOF marker + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + } + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +static void +get_anno_sub(IFFByteStream &iff, IFFByteStream &out) +{ + GUTF8String chkid; + while (iff.get_chunk(chkid)) + { + if (iff.composite()) + get_anno_sub(iff, out); + else if (chkid == "ANTa" || chkid == "ANTz" || + chkid == "TXTa" || chkid == "TXTz" ) + { + out.put_chunk(chkid); + out.copy(*iff.get_bytestream()); + out.close_chunk(); + } + iff.close_chunk(); + } +} + +static GP<ByteStream> +get_anno(GP<DjVuFile> f) +{ + if (! f->anno) + { + GP<ByteStream> bs = f->get_init_data_pool()->get_stream(); + GP<ByteStream> anno = ByteStream::create(); + GP<IFFByteStream> in = IFFByteStream::create(bs); + GP<IFFByteStream> out = IFFByteStream::create(anno); + get_anno_sub(*in, *out); + f->anno = anno; + } + f->anno->seek(0); + return f->anno; +} + +static GP<DjVuTXT> +get_text(GP<DjVuFile> file) +{ + GUTF8String chkid; + GP<IFFByteStream> iff = IFFByteStream::create(get_anno(file)); + while (iff->get_chunk(chkid)) + { + if (chkid == "TXTa") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + txt->decode(iff->get_bytestream()); + return txt; + } + else if (chkid == "TXTz") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream()); + txt->decode(bsiff); + return txt; + } + iff->close_chunk(); + } + return 0; +} + +static void +print_ps_string(const char *data, int length, ByteStream &out) +{ + while (*data && length>0) + { + int span = 0; + while (span<length && data[span]>=0x20 && data[span]<0x7f + && data[span]!='(' && data[span]!=')' && data[span]!='\\' ) + span++; + if (span > 0) + { + out.write(data, span); + data += span; + length -= span; + } + else + { + char buffer[5]; + sprintf(buffer,"\\%03o", *data); + out.write(buffer,4); + data += 1; + length -= 1; + } + } +} + +static void +print_txt_sub(DjVuTXT &txt, DjVuTXT::Zone &zone, + ByteStream &out,int &lastx,int &lasty) +{ + // Get separator + char separator = 0; + switch(zone.ztype) + { + case DjVuTXT::COLUMN: + separator = DjVuTXT::end_of_column; break; + case DjVuTXT::REGION: + separator = DjVuTXT::end_of_region; break; + case DjVuTXT::PARAGRAPH: + separator = DjVuTXT::end_of_paragraph; break; + case DjVuTXT::LINE: + separator = DjVuTXT::end_of_line; break; + case DjVuTXT::WORD: + separator = ' '; break; + default: + separator = 0; break; + } + // Zone children + if (zone.children.isempty()) + { + const char *data = (const char*)txt.textUTF8 + zone.text_start; + int length = zone.text_length; + if (data[length-1] == separator) + length -= 1; + out.write("( ",2); + print_ps_string(data,length,out); + out.write(")",1); + GUTF8String message; + int tmpx= zone.rect.xmin-lastx; + int tmpy= zone.rect.ymin-lasty; + message.format(" %d %d S \n", tmpx, tmpy); + lastx=zone.rect.xmin; + lasty=zone.rect.ymin; + out.write((const char*)message, message.length()); + } + else + { + if (zone.ztype==DjVuTXT::LINE) + { + GUTF8String message; + message.format("%d F\n",zone.rect.ymax-zone.rect.ymin); + out.write((const char*)message,message.length()); + } + for (GPosition pos=zone.children; pos; ++pos) + print_txt_sub(txt, zone.children[pos], out,lastx,lasty); + } +} + +static void +print_txt(GP<DjVuTXT> txt, + ByteStream &out ) +{ + if (txt) + { + int lastx=0; + int lasty=0; + GUTF8String message = + "%% -- now doing hidden text\n" + "gsave -1 -1 0 0 clip 0 0 moveto\n"; + out.write((const char*)message,message.length()); + print_txt_sub(*txt, txt->page_zone, out,lastx,lasty); + message = + "grestore \n"; + out.write((const char*)message,message.length()); + } +} + +void +DjVuToPS:: +print_image(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + GP<DjVuTXT> txt) +{ + /* Just outputs the specified image. The function assumes, that + all add-ons (like {\em document setup}, {\em page setup}) are + already there. It will just output the image. Since + output of this function will generate PostScript errors when + used without output of auxiliary functions, it should be + used carefully. */ + DEBUG_MSG("DjVuToPS::print_image(): Printing DjVuImage to a stream\n"); + DEBUG_MAKE_INDENT(3); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (prn_progress_cb) + prn_progress_cb(0, prn_progress_cl_data); + // Compute information for chosen display mode + print_txt(txt, str); + make_gamma_ramp(dimg); + if (options.get_level() < 2) + { + print_image_lev1(str, dimg, prn_rect); + } + else if (options.get_level() < 3 && dimg->get_fgpm()) + { + switch(options.get_mode()) + { + case Options::COLOR: + case Options::FORE: + print_image_lev2(str, dimg, prn_rect); + break; + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + else + { + switch(options.get_mode()) + { + case Options::COLOR: + print_bg(str, dimg, prn_rect); + print_fg(str, dimg, prn_rect); + break; + case Options::FORE: + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + if (prn_progress_cb) + prn_progress_cb(1, prn_progress_cl_data); +} + + + + +// *********************************************************************** +// ******* PUBLIC FUNCTION FOR PRINTING A SINGLE PAGE ******************** +// *********************************************************************** + + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect_in, + const GRect &img_rect, + int override_dpi) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu page to a stream\n"); + DEBUG_MAKE_INDENT(3); + GRect prn_rect; + prn_rect.intersect(prn_rect_in, img_rect); + DEBUG_MSG("prn_rect=(" << prn_rect.xmin << ", " << prn_rect.ymin << ", " << + prn_rect.width() << ", " << prn_rect.height() << ")\n"); + DEBUG_MSG("img_rect=(" << img_rect.xmin << ", " << img_rect.ymin << ", " << + img_rect.width() << ", " << img_rect.height() << ")\n"); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (img_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.bad_scale")); + GRectMapper mapper; + mapper.set_input(img_rect); + GRect full_rect(0, 0, dimg->get_width(), dimg->get_height()); + mapper.set_output(full_rect); + mapper.map(prn_rect); + int image_dpi = dimg->get_dpi(); + if (override_dpi>0) + image_dpi = override_dpi; + if (image_dpi <= 0) + image_dpi = 300; + store_doc_prolog(str, 1, (int)(image_dpi), &prn_rect); + store_doc_setup(str); + write(str,"%%%%Page: 1 1\n"); + store_page_setup(str, (int)(image_dpi), prn_rect); + print_image(str, dimg, prn_rect, 0); + store_page_trailer(str); + write(str,"showpage\n"); + store_doc_trailer(str); +} + + + + +// *********************************************************************** +// *************************** DOCUMENT LEVEL **************************** +// *********************************************************************** + + +void +DjVuToPS:: +parse_range(GP<DjVuDocument> doc, + GUTF8String page_range, + GList<int> &pages_todo) +{ + int doc_pages = doc->get_pages_num(); + if (!page_range.length()) + page_range.format("1-%d", doc_pages); + DEBUG_MSG("page_range='" << (const char *)page_range << "'\n"); + int spec = 0; + int both = 1; + int start_page = 1; + int end_page = doc_pages; + const char *q = (const char*)page_range; + char *p = (char*)q; + while (*p) + { + while (*p==' ') + p += 1; + if (! *p) + break; + if (*p>='0' && *p<='9') + { + end_page = strtol(p, &p, 10); + spec = 1; + } + else if (*p=='$') + { + spec = 1; + end_page = doc_pages; + p += 1; + } + else if (both) + { + end_page = 1; + } + else + { + end_page = doc_pages; + } + while (*p==' ') + p += 1; + if (both) + { + start_page = end_page; + if (*p == '-') + { + p += 1; + both = 0; + continue; + } + } + both = 1; + while (*p==' ') + p += 1; + if (*p && *p != ',') + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + GUTF8String(p) ); + if (*p == ',') + p += 1; + if (! spec) + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + page_range ); + spec = 0; + if (end_page < 0) + end_page = 0; + if (start_page < 0) + start_page = 0; + if (end_page > doc_pages) + end_page = doc_pages; + if (start_page > doc_pages) + start_page = doc_pages; + if (start_page <= end_page) + for(int page_num=start_page; page_num<=end_page; page_num++) + pages_todo.append(page_num-1); + else + for(int page_num=start_page; page_num>=end_page; page_num--) + pages_todo.append(page_num-1); + } +} + +class DjVuToPS::DecodePort : public DjVuPort +{ +protected: + DecodePort(void); +public: + static GP<DecodePort> create(void); + GEvent decode_event; + bool decode_event_received; + double decode_done; + GURL decode_page_url; + virtual void notify_file_flags_changed(const DjVuFile*,long,long); + virtual void notify_decode_progress(const DjVuPort*,double); +}; + +DjVuToPS::DecodePort:: +DecodePort(void) + : decode_event_received(false), + decode_done((double)0) +{ +} + +GP<DjVuToPS::DecodePort> +DjVuToPS::DecodePort:: +create(void) +{ + return new DecodePort; +} + +void +DjVuToPS::DecodePort:: +notify_file_flags_changed(const DjVuFile *source, + long set_mask, long clr_mask) +{ + // WARNING! This function is called from another thread + if (set_mask & (DjVuFile::DECODE_OK | + DjVuFile::DECODE_FAILED | + DjVuFile::DECODE_STOPPED )) + { + if (source->get_url() == decode_page_url) + { + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS::DecodePort:: +notify_decode_progress(const DjVuPort *source, double done) +{ + // WARNING! This function is called from another thread + if (source->inherits("DjVuFile")) + { + DjVuFile * file=(DjVuFile *) source; + if (file->get_url()==decode_page_url) + if ((int) (decode_done*20)!=(int) (done*20)) + { + decode_done=done; + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS:: +set_refresh_cb(void (*_refresh_cb)(void*), void *_refresh_cl_data) +{ + refresh_cb = _refresh_cb; + refresh_cl_data = _refresh_cl_data; +} + +void +DjVuToPS:: +set_prn_progress_cb(void (*_prn_progress_cb)(double, void *), + void *_prn_progress_cl_data) +{ + prn_progress_cb=_prn_progress_cb; + prn_progress_cl_data=_prn_progress_cl_data; +} + +void +DjVuToPS:: +set_dec_progress_cb(void (*_dec_progress_cb)(double, void *), + void *_dec_progress_cl_data) +{ + dec_progress_cb=_dec_progress_cb; + dec_progress_cl_data=_dec_progress_cl_data; +} + +void +DjVuToPS:: +set_info_cb(void (*_info_cb)(int, int, int, Stage, void*), + void *_info_cl_data) +{ + info_cb=_info_cb; + info_cl_data=_info_cl_data; +} + +GP<DjVuImage> +DjVuToPS:: +decode_page(GP<DjVuDocument> doc, + int page_num, int cnt, int todo) +{ + DEBUG_MSG("processing page #" << page_num << "\n"); + if (! port) + { + port = DecodePort::create(); + DjVuPort::get_portcaster()->add_route((DjVuDocument*)doc, port); + } + port->decode_event_received = false; + port->decode_done = 0; + GP<DjVuFile> djvu_file; + GP<DjVuImage> dimg; + if (page_num >= 0 && page_num < doc->get_pages_num()) + djvu_file = doc->get_djvu_file(page_num); + if (! djvu_file ) + return 0; + if (djvu_file->is_decode_ok()) + return doc->get_page(page_num, false); + // This is the best place to call info_cb(). Note, that + // get_page() will start decoding if necessary, and will not + // return until the decoding is over in a single threaded + // environment. That's why we call get_djvu_file() first. + if (info_cb) + info_cb(page_num, cnt, todo, DECODING, info_cl_data); + // Do NOT decode the page synchronously here!!! + // The plugin will deadlock otherwise. + dimg = doc->get_page(page_num, false); + djvu_file = dimg->get_djvu_file(); + port->decode_page_url = djvu_file->get_url(); + if (djvu_file->is_decode_ok()) + return dimg; + DEBUG_MSG("decoding\n"); + if (dec_progress_cb) + dec_progress_cb(0, dec_progress_cl_data); + while(! djvu_file->is_decode_ok()) + { + while(!port->decode_event_received && + !djvu_file->is_decode_ok()) + { + port->decode_event.wait(250); + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + port->decode_event_received = false; + if (djvu_file->is_decode_failed() || + djvu_file->is_decode_stopped()) + G_THROW(ERR_MSG("DjVuToPS.no_image") + + GUTF8String("\t") + + GUTF8String(page_num)); + if (dec_progress_cb) + dec_progress_cb(port->decode_done, dec_progress_cl_data); + } + if (dec_progress_cb) + dec_progress_cb(1, dec_progress_cl_data); + return dimg; +} + +void +DjVuToPS:: +process_single_page(ByteStream &str, + GP<DjVuDocument> doc, + int page_num, int cnt, int todo, + int magic) +{ + GP<DjVuTXT> txt; + GP<DjVuImage> dimg; + dimg = decode_page(doc, page_num, cnt, todo); + if (options.get_text()) + txt = get_text(dimg->get_djvu_file()); + if (info_cb) + info_cb(page_num, cnt, todo, PRINTING, info_cl_data); + if (!magic) + write(str, "%%%%Page: %d %d\n", page_num+1, cnt+1); + if (dimg) + { + int dpi = dimg->get_dpi(); + dpi = ((dpi <= 0) ? 300 : dpi); + GRect img_rect(0, 0, dimg->get_width(), dimg->get_height()); + store_page_setup(str, dpi, img_rect, magic); + print_image(str, dimg, img_rect,txt); + store_page_trailer(str); + } + if (!magic) + write(str,"showpage\n"); +} + + +struct pdata { + int page1, page2; + int smax, spos; + int offset; +}; + +void +DjVuToPS:: +process_double_page(ByteStream &str, + GP<DjVuDocument> doc, + void *v, int cnt, int todo) +{ + const pdata *inf = (const pdata*)v; + int off = abs(inf->offset); + write(str, + "%%%%Page: (%d,%d) %d\n" + "gsave\n" + "/fold-dict 8 dict dup 3 1 roll def begin\n" + " clippath pathbbox newpath pop pop translate\n" + " clippath pathbbox newpath 4 2 roll pop pop\n" + " /ph exch def\n" + " /pw exch def\n" + " /w ph %d sub 2 div def\n" + " /m1 %d def\n" + " /m2 %d def\n" + "end\n", + inf->page1 + 1, inf->page2 + 1, cnt, + 2 * (off + options.get_bookletfold(inf->smax-1)), + inf->offset + options.get_bookletfold(inf->spos), + inf->offset - options.get_bookletfold(inf->spos)); + if (options.get_cropmarks()) + write(str, + "%% -- folding marks\n" + "fold-dict begin\n" + " 0 setgray 0.5 setlinewidth\n" + " ph m1 m2 add add 2 div dup\n" + " 0 exch moveto 36 0 rlineto stroke\n" + " pw exch moveto -36 0 rlineto stroke\n" + "end\n"); + write(str, + "%% -- first page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div w add m1 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page1 >= 0) + process_single_page(str, doc, inf->page1, cnt*2, todo*2, +1); + write(str, + "grestore\n" + "%% -- second page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div m2 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page2 >= 0) + process_single_page(str, doc, inf->page2, cnt*2+1, todo*2, -1); + write(str, + "grestore\n" + "grestore\n" + "showpage\n"); +} + +static void +booklet_order(GList<int>& pages, int smax) +{ + // -- make a multiple of four + while (pages.size() & 0x3) + pages.append(-1); + // -- copy to array + int i = 0; + int n = pages.size(); + GTArray<int> p(0,n-1); + for (GPosition pos=pages; pos; ++pos) + p[i++] = pages[pos]; + // -- rebuild + pages.empty(); + for (i=0; i<n; i+=smax) + { + int lo = i; + int hi = i+smax-1; + if (hi >= n) + hi = n-1; + while (lo < hi) + { + pages.append(p[hi--]); + pages.append(p[lo++]); + pages.append(p[lo++]); + pages.append(p[hi--]); + } + } +} + + +// *********************************************************************** +// ******* PUBLIC FUNCTIONS FOR PRINTING MULTIPLE PAGES ****************** +// *********************************************************************** + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuDocument> doc, + GUTF8String page_range) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu document\n"); + DEBUG_MAKE_INDENT(3); + // Get page range + GList<int> pages_todo; + parse_range(doc, page_range, pages_todo); + int todo = pages_todo.size(); + if (options.get_format()==Options::EPS) + { + /* Encapsulated Postscript mode */ + if (todo != 1) + G_THROW(ERR_MSG("DjVuToPS.only_one_page")); + GPosition pos = pages_todo; + int page_num = pages_todo[pos]; + GP<DjVuImage> dimg = decode_page(doc,page_num,0,todo); + if (! dimg) + G_THROW(ERR_MSG("DjVuToPS.no_image") + GUTF8String("\t1")); + GRect bbox(0, 0, dimg->get_width(), dimg->get_height()); + store_doc_prolog(str, 1, dimg->get_dpi(), &bbox); + store_doc_setup(str); + process_single_page(str, doc, page_num, 0, todo, 0); + } + else if (options.get_bookletmode()==Options::OFF) + { + /* Normal mode */ + int cnt = 0; + store_doc_prolog(str, todo, 0, 0); + store_doc_setup(str); + for(GPosition pos = pages_todo; pos; ++pos) + process_single_page(str,doc,pages_todo[pos],cnt++,todo,0); + store_doc_trailer(str); + } + else + { + /* Booklet mode */ + int sheets_left = (todo+3)/4; + int sides_todo = sheets_left; + if (options.get_bookletmode() == Options::RECTOVERSO) + sides_todo *= 2; + int sheets_max = (options.get_bookletmax()+3)/4; + if (! sheets_max) + sheets_max = sheets_left; + // -- reorder pages + booklet_order(pages_todo, sheets_max*4); + // -- print + int sides = 0; + int sheetpos = sheets_max; + store_doc_prolog(str, sides_todo, 0, 0); + store_doc_setup(str); + for (GPosition p=pages_todo; p; ++p) + { + struct pdata inf; + inf.page1 = pages_todo[p]; + inf.page2 = pages_todo[++p]; + inf.smax = sheets_max; + inf.spos = --sheetpos; + inf.offset = options.get_bookletalign(); + if (options.get_bookletmode() != Options::VERSO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + inf.page1 = pages_todo[++p]; + inf.page2 = pages_todo[++p]; + inf.offset = -inf.offset; + if (options.get_bookletmode() != Options::RECTO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + sheets_left -= 1; + if (sheetpos <= 0) + sheetpos = ((sheets_max<sheets_left) ? sheets_max : sheets_left); + } + store_doc_trailer(str); + } +} + + +void +DjVuToPS:: +print(ByteStream &str, GP<DjVuDocument> doc) +{ + GUTF8String dummy; + print(str,doc,dummy); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h new file mode 100644 index 00000000..95d547bb --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.h @@ -0,0 +1,425 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002-2003 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuToPS.h,v 1.14 2004/03/05 16:48:53 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DJVU_TO_PS_H_ +#define _DJVU_TO_PS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name DjVuToPS.h + Files #"DjVuToPS.h"# and #"DjVuToPS.cpp"# implement code that can be + used to convert a \Ref{DjVuImage} or \Ref{DjVuDocument} to PostScript + format. The conversion is carried out by the \Ref{DjVuToPS} class. + + @memo PostScript file generator + @author Andrei Erofeev <[email protected]> \\ + Florin Nicsa <[email protected]> + @version + #$Id: DjVuToPS.h,v 1.14 2004/03/05 16:48:53 leonb Exp $# +*/ +//@{ + +#include "DjVuGlobal.h" +#include "GRect.h" +#include "DjVuDocument.h" +#include "DjVuText.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** DjVuImage to PostScript converter. + Use this class to print \Ref{DjVuImage}s and \Ref{DjVuDocument}s. + The behavior is customizable. See \Ref{DjVuToPS::Options} for the + description of available options.*/ +class DjVuToPS +{ +public: + class DecodePort; + + /** DjVuToPS options. Use this class to customize the way + in which DjVu to PS conversion will be done. You can adjust + the following things: + \begin{description} + \item[Format] ({\em EPS} or {\em PS}). Use the {\em EPS} + format if you plan to embed the output image into another + document. Print {\em PS} otherwise. + \item[Language level] ({\em 1} or {\em 2}). Any PostScript + printer or interpreter should understand PostScript Level 1 + files. Unfortunately we cannot efficiently compress and encode + data when generating Level 1 files. PostScript Level 2 allows + to employ an RLE compression and ASCII85 encoding scheme, + which makes output files significantly smaller. Most of + the printers and word processors nowadays support PostScript + Level 2. + \item[Orientation] ({\em PORTRAIT} or {\em LANDSCAPE}) + \item[Zoom factor] ({\em FIT_PAGE} or {\em ONE_TO_ONE}). + {\em ONE_TO_ONE} mode is useful, if you want the output to + be of the same size as the original image (before compression). + This requires that the #dpi# setting inside the \Ref{DjVuImage} + is correct. In most of the cases the {\em FIT_PAGE} zoom is + would be your best choice. + \item[Mode] ({\em COLOR}, {\em FORE}, {\em BACK}, or {\em BW}) + Specifies how the \Ref{DjVuImage}s will be rendered (all layers, + foreground layer, background layer, and the mask respectively) + \item[Color] ({\em TRUE} or {\em FALSE}). Choosing {\em FALSE} + converts color images to gray scale. + \item[Gamma] Printer color correction. + This parameter ranges from #0.3# to #5.0#. + \item[sRGB] ({\em TRUE} or {\em FALSE}). Choosing {\em TRUE} + enables accurate sRGB color calibration. This option + only works with language level 2. When this is set, + gamma correction is clamped to #2.2#. + \item[Number of copies] Specifies how many copies should be + printed. This does {\bf not} affect the size of the output file. + \end{description} + */ + class Options + { + public: + /** Specifies the rendering mode */ + enum Mode { COLOR, FORE, BACK, BW }; + /** Selects the output format */ + enum Format { PS, EPS }; + /** Specifies the orientation of the output image */ + enum Orientation { PORTRAIT, LANDSCAPE, AUTO }; + /** Specifies the booklet mode */ + enum BookletMode { OFF, RECTO, VERSO, RECTOVERSO }; + private: + Format format; + int level; + Orientation orientation; + Mode mode; + int zoom; + bool color; + bool calibrate; + bool text; + double gamma; + int copies; + bool frame; + bool cropmarks; + BookletMode bookletmode; + int bookletmax; + int bookletalign; + int bookletfold; + int bookletxfold; + public: + /** Sets output image format to #PS# or #EPS# */ + void set_format(Format format); + /** Sets PostScript level (#1#, #2#, or #3#) */ + void set_level(int level); + /** Sets orientation (#LANDSCAPE# or #PORTRAIT#) */ + void set_orientation(Orientation orientation); + /** Sets \Ref{DjVuImage} rendering mode (#COLOR#, #BW#, #FORE#, #BACK#) */ + void set_mode(Mode mode); + /** Sets zoom factor. Zoom 0 means fit page. */ + void set_zoom(int zoom); + /** Affects automatic conversion to GreyScale mode. */ + void set_color(bool color); + /** Sets gamma correction factor. Ranges from #0.3# to #5.0#. */ + void set_gamma(double gamma); + /** Sets sRGB color calibration flag. */ + void set_sRGB(bool calibrate); + /** Specifies the number of copies to be printed. */ + void set_copies(int copies); + /** Specifies if a gray frame around the image should be printed. */ + void set_frame(bool on); + /** Specifies if crop marks should be printed. */ + void set_cropmarks(bool on); + /** Specifies if a shadow text should be printed. */ + void set_text(bool on); + /** Specifies the bookletmode */ + void set_bookletmode(BookletMode m); + /** Specifies the maximal number of pages in a booklet */ + void set_bookletmax(int m); + /** Specifies an offset (points) between + booklet recto(s) and verso(s). */ + void set_bookletalign(int m); + /** Specifies the margin (points) required to fold + the booklet (#fold# in points) and the margin + increase required for each sheet (#xfold# in millipoints). */ + void set_bookletfold(int fold, int xfold=0); + + /** Returns output image format (#PS# or #EPS#) */ + Format get_format(void) const { + return format; } + /** Returns PostScript level (#1# or #2#) */ + int get_level(void) const { + return level; } + /** Returns output image orientation (#PORTRAIT# or #LANDSCAPE#) */ + Orientation get_orientation(void) const { + return orientation; } + /** Returns \Ref{DjVuImage} rendering mode + (#COLOR#, #FORE#, #BACK#, #BW#) */ + Mode get_mode(void) const { + return mode; } + /** Returns output zoom factor (#FIT_PAGE# or #ONE_TO_ONE#) */ + int get_zoom(void) const { + return zoom; } + /** Returns color printing flag. */ + bool get_color(void) const { + return color; } + /** Returns sRGB color calibration flag. */ + bool get_sRGB(void) const { + return calibrate; } + /** Returns printer gamma correction factor */ + double get_gamma(void) const { + return ((calibrate) ? ((double)2.2) : gamma); } + /** Returns the number of copies, which will be printed by printer + This parameter does {\bf not} affect the size of output file */ + int get_copies(void) const { + return copies; } + /** Returns #TRUE# if there will be a gray frame */ + bool get_frame(void) const { + return frame; } + /** Returns #TRUE# if there will be a gray frame */ + bool get_cropmarks(void) const { + return cropmarks; } + /** Returns #TRUE# if there will be a shadow text printed */ + bool get_text(void) const { + return text; } + /** Returns the booklet mode */ + BookletMode get_bookletmode(void) const { + return bookletmode; } + /** Returns the booklet max size */ + int get_bookletmax(void) { + return bookletmax; } + /** Returns the booklet recto/verso offset */ + int get_bookletalign(void) { + return bookletalign; } + /** Returns the folding margin for sheet number #n#. */ + int get_bookletfold(int n=0) { + return bookletfold + (n*bookletxfold+500)/1000; } + /* Constructor */ + Options(void); + }; + + /** Describes current page processing stage. This is passed to + the #info_cb()# callback. See \Ref{set_info_cb}() for details. */ + enum Stage { DECODING, PRINTING }; + +private: + void (*refresh_cb)(void*); + void *refresh_cl_data; + void (*prn_progress_cb)(double, void*); + void *prn_progress_cl_data; + void (*dec_progress_cb)(double, void*); + void *dec_progress_cl_data; + void (*info_cb)(int,int,int,Stage,void*); + void *info_cl_data; + unsigned char ramp[256]; + GP<DecodePort> port; +protected: + void store_doc_prolog(ByteStream&,int,int,GRect*); + void store_doc_setup(ByteStream&); + void store_doc_trailer(ByteStream&); + void store_page_setup(ByteStream&,int,const GRect&,int align=0); + void store_page_trailer(ByteStream&); + void make_gamma_ramp(GP<DjVuImage>); + void print_image_lev1(ByteStream&,GP<DjVuImage>,const GRect&); + void print_image_lev2(ByteStream&,GP<DjVuImage>,const GRect&); + void print_bg(ByteStream&,GP<DjVuImage>,const GRect&); + void print_fg_3layer(ByteStream&,GP<DjVuImage>,const GRect&,unsigned char*); + void print_fg_2layer(ByteStream&,GP<DjVuImage>,const GRect&,unsigned char*); + void print_fg(ByteStream&,GP<DjVuImage>,const GRect&); + void print_image(ByteStream&,GP<DjVuImage>,const GRect&,GP<DjVuTXT>); + void parse_range(GP<DjVuDocument>,GUTF8String,GList<int>&); + GP<DjVuImage> decode_page(GP<DjVuDocument>,int,int,int); + void process_single_page(ByteStream&,GP<DjVuDocument>,int,int,int,int); + void process_double_page(ByteStream&,GP<DjVuDocument>,void*,int,int); + +public: + /** Options affecting the print result. Please refer to + \Ref{DjVuToPS::Options} for details. */ + Options options; + + /** @name Callbacks */ + //@{ + /** Refresh callback is a function, which will be called fairly + often while the image (document) is being printed. It can + be used to refresh a GUI, if necessary. + + @param refresh_cb Callback function to be called periodically + @param refresh_cl_data Pointer passed to #refresh_cb()# */ + void set_refresh_cb(void (*refresh_cb)(void*), void *refresh_cl_data); + /** Callback used to report the progress of printing. The progress is a + double number from 0 to 1. If an existing \Ref{DjVuImage} is printed, + this callback will be called at least twice: in the beginning and at the + end of printing. If a \Ref{DjVuDocument} is being printed, this + callback will be used to report printing progress of every page. To + learn the number of the page being printed you can use + \Ref{set_info_cb}() function. See \Ref{set_dec_progress_cb}() to find + out how to learn the decoding progress. + + @param cb Callback function to be called + @param data Pointer passed to #cb()#. */ + void set_prn_progress_cb(void (*cb)(double, void*), void *data); + /** Callback used to report the progress of decoding. The progress is a + double number from 0 to 1. This callback is only used when printing a + \Ref{DjVuDocument} in a multithreaded environment. In all other cases it + will not be called. Whenever you \Ref{print}() a page range from a + \Ref{DjVuDocument}, the #DjVuToPS# has to decode the mentioned pages + before writing them to the output \Ref{ByteStream}. This callback can be + helpful to find out the status of the decoding. See + \Ref{set_prn_progress_cb}() to find out how to learn the printing + progress. See \Ref{set_info_cb}() to learn how to find out the number + of the page being processed, the total number of pages and the number of + processed pages. + + @param cb Callback function to be called + @param data Pointer passed to #cb()#. */ + void set_dec_progress_cb(void (*cb)(double, void*), void *data); + /** Callback used to report the current printing stage of a + \Ref{DjVuDocument}. When printing a \Ref{DjVuDocument} ({\bf not} a + \Ref{DjVuImage}), the #DjVuToPS# class will decode and output every page + mentioned in the {\em page range}. Before decoding and outputing, it + will call this #info_cb()# callback in order to let you know about what + is going on. This can be quite useful in a GUI program to keep the user + informed. This function is not called when you print a \Ref{DjVuImage}. + + Description of the arguments passed to #info_cb#: + \begin{description} + \item[page_num] The number of the page being processed + \item[page_cnt] Counts how many pages have already been processed. + \item[tot_pages] Counts how many pages will be output enventually. + \item[stage] Describes the current processing stage + (#DECODING# or #PRINTING#). + \end{description} + @param cb Callback function to be called + @param data Pointer, which will be passed to #cb()#. */ + void set_info_cb(void (*cb)(int,int,int,Stage,void*), void *data); + //@} + + /** Prints the specified \Ref{DjVuImage} #dimg# into the + \Ref{ByteStream} #str#. The function will first scale + the image to fit the #img_rect#, then extract #prn_rect# + from the obtained bitmap and will output it in the + PostScript format. The function generates a legal PostScript + (or Encapsulated PostScript) file taking care of all comments + conforming to Document Structure Conventions v. 3.0. + + {\bf Warning:} The zoom factor specified in \Ref{Options} does + not affect the amount of data stored into the PostScript file. + It will be used by the PostScript code to additionally scale + the image. We cannot pre-scale it here, because we do not know + the future resolution of the printer. The #img_rect# and + #prn_rect# alone define how much data will be sent to printer. + + Using #img_rect# one can upsample or downsample the image prior + to sending it to the printer. + + @param str \Ref{ByteStream} where PostScript output will be sent + @param dimg \Ref{DjVuImage} to print + @param img_rect Rectangle to which the \Ref{DjVuImage} will be scaled. + Note that this parameters defines the amount of data + that will actually be sent to the printer. The PostScript + code can futher resize the image according to the + #zoom# parameter from the \Ref{Options} structure. + @param prn_rect Part of img_rect to send to printer. + @param override_dpi Optional parameter allowing you to override + dpi setting that would otherwise be extracted from #dimg# */ + void print(ByteStream&, GP<DjVuImage> dimg, + const GRect &prn_rect, const GRect &img_rect, + int override_dpi=-1 ); + + /** Outputs the specifies pages from the \Ref{DjVuDocument} into the + \Ref{ByteStream} in PostScript format. The function will generate a + multipage PostScript document conforming to PS DSC 3.0 by storing into + it every page mentioned in the #page_range#. + + If #page_range# is empty, all pages from the \Ref{DjVuDocument} #doc# + will be printed. The #page_range# is a set of ranges separated by + commas. Every range has this form: {\em start_page}[-{\em + end_page}]. {\em end_page} is optional and can be less than the {\em + start_page}, in which case the pages will be printed in the reverse + order. + + Examples: + \begin{itemize} + \item {\bf 1-10} - Will print pages 1 to 10. + \item {\bf 10-1} - Will print pages 1 to 10 in reverse order. + \item {\bf 1-10,12-20} - Will print pages 1 to 20 with page 11 skipped. + \end{itemize} */ + void print(ByteStream&, GP<DjVuDocument> doc, GUTF8String page_range); + void print(ByteStream&, GP<DjVuDocument> doc); + + + /** Default constructor. Initializes the class. */ + DjVuToPS(void); +}; + + +//**************************************************************************** +//******************************** DjVuToPS ********************************** +//**************************************************************************** + +//@} +// ------------ + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp b/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp new file mode 100644 index 00000000..696367e7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GBitmap.cpp @@ -0,0 +1,1658 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GBitmap.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GBitmap.h" +#include "ByteStream.h" +#include "GRect.h" +#include "GString.h" +#include "GThreads.h" +#include "GException.h" +#include <string.h> + +// File "$Id: GBitmap.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $" +// - Author: Leon Bottou, 05/1997 + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// ----- constructor and destructor + +GBitmap::~GBitmap() +{ +} + +void +GBitmap::destroy(void) +{ + gbytes_data.resize(0); + bytes = 0; + grle.resize(0); + grlerows.resize(0); + rlelength = 0; +} + +GBitmap::GBitmap() + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ +} + +GBitmap::GBitmap(int nrows, int ncolumns, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(nrows, ncolumns, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(ByteStream &ref, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(const GBitmap &ref) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, ref.border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GBitmap::GBitmap(const GBitmap &ref, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + +GBitmap::GBitmap(const GBitmap &ref, const GRect &rect, int border) + : nrows(0), ncolumns(0), border(0), + bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data), + grle(rle), grlerows(rlerows), rlelength(0), + monitorptr(0) +{ + G_TRY + { + init(ref, rect, border); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + + + + + +// ----- initialization + +void +GBitmap::init(int arows, int acolumns, int aborder) +{ + GMonitorLock lock(monitor()); + destroy(); + grays = 2; + nrows = arows; + ncolumns = acolumns; + border = aborder; + bytes_per_row = ncolumns + border; + int npixels = nrows * bytes_per_row + border; + gzerobuffer=zeroes(bytes_per_row + border); + if (npixels > 0) + { + gbytes_data.resize(npixels); + gbytes_data.clear(); + bytes = bytes_data; + } +} + + +void +GBitmap::init(const GBitmap &ref, int aborder) +{ + GMonitorLock lock(monitor()); + if (this != &ref) + { + GMonitorLock lock(ref.monitor()); + init(ref.nrows, ref.ncolumns, aborder); + grays = ref.grays; + unsigned char *row = bytes_data+border; + for (int n=0; n<nrows; n++, row+=bytes_per_row) + memcpy( (void*)row, (void*)ref[n], ncolumns ); + } + else if (aborder > border) + { + minborder(aborder); + } +} + + +void +GBitmap::init(const GBitmap &ref, const GRect &rect, int border) +{ + GMonitorLock lock(monitor()); + // test bitmap physical equality + if (this == &ref) + { + GBitmap tmp; + tmp.grays = grays; + tmp.border = border; + tmp.bytes_per_row = bytes_per_row; + tmp.ncolumns = ncolumns; + tmp.nrows = nrows; + tmp.bytes = bytes; + tmp.gbytes_data.swap(gbytes_data); + tmp.grle.swap(grle); + bytes = 0 ; + init(tmp, rect, border); + } + else + { + GMonitorLock lock(ref.monitor()); + // create empty bitmap + init(rect.height(), rect.width(), border); + grays = ref.grays; + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + unsigned char *dst = (*this)[y]; + const unsigned char *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = src[x]; + } + } + } +} + + +void +GBitmap::init(ByteStream &ref, int aborder) +{ + GMonitorLock lock(monitor()); + // Get magic number + char magic[2]; + magic[0] = magic[1] = 0; + ref.readall((void*)magic, sizeof(magic)); + char lookahead = '\n'; + int acolumns = read_integer(lookahead, ref); + int arows = read_integer(lookahead, ref); + init(arows, acolumns, aborder); + // go reading file + if (magic[0]=='P') + { + switch(magic[1]) + { + case '1': + grays = 2; + read_pbm_text(ref); + return; + case '2': + grays = 1 + read_integer(lookahead, ref); + if (grays > 256) + G_THROW("Cannot read PGM with depth greater than 8 bits."); + read_pgm_text(ref); + return; + case '4': + grays = 2; + read_pbm_raw(ref); + return; + case '5': + grays = 1 + read_integer(lookahead, ref); + if (grays > 256) + grays = 256; + read_pgm_raw(ref); + return; + } + } + else if (magic[0]=='R') + { + switch(magic[1]) + { + case '4': + grays = 2; + read_rle_raw(ref); + return; + } + } + G_THROW( ERR_MSG("GBitmap.bad_format") ); +} + +void +GBitmap::donate_data(unsigned char *data, int w, int h) +{ + destroy(); + grays = 2; + nrows = h; + ncolumns = w; + border = 0; + bytes_per_row = w; + gbytes_data.replace(data,w*h); + bytes = bytes_data; + rlelength = 0; +} + +void +GBitmap::donate_rle(unsigned char *rledata, unsigned int rledatalen, int w, int h) +{ + destroy(); + grays = 2; + nrows = h; + ncolumns = w; + border = 0; + bytes_per_row = w; +// rle = rledata; + grle.replace(rledata,rledatalen); + rlelength = rledatalen; +} + + +unsigned char * +GBitmap::take_data(size_t &offset) +{ + GMonitorLock lock(monitor()); + unsigned char *ret = bytes_data; + if (ret) offset = (size_t)border; + bytes_data=0; + return ret; +} + +const unsigned char * +GBitmap::get_rle(unsigned int &rle_length) +{ + if(!rle) + compress(); + rle_length=rlelength; + return rle; +} + +// ----- compression + + +void +GBitmap::compress() +{ + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_compress") ); + GMonitorLock lock(monitor()); + if (bytes) + { + grle.resize(0); + grlerows.resize(0); + rlelength = encode(rle,grle); + if (rlelength) + { + gbytes_data.resize(0); + bytes = 0; + } + } +} + +void +GBitmap::uncompress() +{ + GMonitorLock lock(monitor()); + if (!bytes && rle) + decode(rle); +} + + + +unsigned int +GBitmap::get_memory_usage() const +{ + unsigned long usage = sizeof(GBitmap); + if (bytes) + usage += nrows * bytes_per_row + border; + if (rle) + usage += rlelength; + return usage; +} + + +void +GBitmap::minborder(int minimum) +{ + if (border < minimum) + { + GMonitorLock lock(monitor()); + if (border < minimum) + { + if (bytes) + { + GBitmap tmp(*this, minimum); + bytes_per_row = tmp.bytes_per_row; + tmp.gbytes_data.swap(gbytes_data); + bytes = bytes_data; + tmp.bytes = 0; + } + border = minimum; + gzerobuffer=zeroes(border + ncolumns + border); + } + } +} + + +#define NMONITORS 8 +static GMonitor monitors[NMONITORS]; + +void +GBitmap::share() +{ + if (!monitorptr) + { + unsigned long x = (unsigned long)this; + monitorptr = &monitors[(x^(x>>5)) % NMONITORS]; + } +} + + +// ----- gray levels + +void +GBitmap::set_grays(int ngrays) +{ + if (ngrays<2 || ngrays>256) + G_THROW( ERR_MSG("GBitmap.bad_levels") ); + // set gray levels + GMonitorLock lock(monitor()); + grays = ngrays; + if (ngrays>2 && !bytes) + uncompress(); +} + +void +GBitmap::change_grays(int ngrays) +{ + GMonitorLock lock(monitor()); + // set number of grays + int ng = ngrays - 1; + int og = grays - 1; + set_grays(ngrays); + // setup conversion table + unsigned char conv[256]; + for (int i=0; i<256; i++) + { + if (i > og) + conv[i] = ng; + else + conv[i] = (i*ng+og/2)/og; + } + // perform conversion + for (int row=0; row<nrows; row++) + { + unsigned char *p = (*this)[row]; + for (int n=0; n<ncolumns; n++) + p[n] = conv[ p[n] ]; + } +} + +void +GBitmap::binarize_grays(int threshold) +{ + GMonitorLock lock(monitor()); + if (bytes) + for (int row=0; row<nrows; row++) + { + unsigned char *p = (*this)[row]; + for(unsigned char const * const pend=p+ncolumns;p<pend;++p) + { + *p = (*p>threshold) ? 1 : 0; + } + } + grays = 2; +} + + +// ----- additive blitting + +#undef min +#undef max + +static inline int +min(int x, int y) +{ + return (x < y ? x : y); +} + +static inline int +max(int x, int y) +{ + return (x > y ? x : y); +} + +void +GBitmap::blit(const GBitmap *bm, int x, int y) +{ + // Check boundaries + if ((x >= ncolumns) || + (y >= nrows) || + (x + (int)bm->columns() < 0) || + (y + (int)bm->rows() < 0) ) + return; + + // Perform blit + GMonitorLock lock1(monitor()); + GMonitorLock lock2(bm->monitor()); + if (bm->bytes) + { + if (!bytes_data) + uncompress(); + // Blit from bitmap + const unsigned char *srow = bm->bytes + bm->border; + unsigned char *drow = bytes_data + border + y*bytes_per_row + x; + for (int sr = 0; sr < bm->nrows; sr++) + { + if (sr+y>=0 && sr+y<nrows) + { + int sc = max(0, -x); + int sc1 = min(bm->ncolumns, ncolumns-x); + while (sc < sc1) + { + drow[sc] += srow[sc]; + sc += 1; + } + } + srow += bm->bytes_per_row; + drow += bytes_per_row; + } + } + else if (bm->rle) + { + if (!bytes_data) + uncompress(); + // Blit from rle + const unsigned char *runs = bm->rle; + unsigned char *drow = bytes_data + border + y*bytes_per_row + x; + int sr = bm->nrows - 1; + drow += sr * bytes_per_row; + int sc = 0; + char p = 0; + while (sr >= 0) + { + const int z = read_run(runs); + if (sc+z > bm->ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + int nc = sc + z; + if (p && sr+y>=0 && sr+y<nrows) + { + if (sc + x < 0) + sc = min(-x, nc); + while (sc < nc && sc + x<ncolumns) + drow[sc++] += 1; + } + sc = nc; + p = 1 - p; + if (sc >= bm->ncolumns) + { + p = 0; + sc = 0; + drow -= bytes_per_row; + sr -= 1; + } + } + } +} + + + +void +GBitmap::blit(const GBitmap *bm, int xh, int yh, int subsample) +{ + // Use code when no subsampling is necessary + if (subsample == 1) + { + blit(bm, xh, yh); + return; + } + + // Check boundaries + if ((xh >= ncolumns * subsample) || + (yh >= nrows * subsample) || + (xh + (int)bm->columns() < 0) || + (yh + (int)bm->rows() < 0) ) + return; + + // Perform subsampling blit + GMonitorLock lock1(monitor()); + GMonitorLock lock2(bm->monitor()); + if (bm->bytes) + { + if (!bytes_data) + uncompress(); + // Blit from bitmap + int dr, dr1, zdc, zdc1; + euclidian_ratio(yh, subsample, dr, dr1); + euclidian_ratio(xh, subsample, zdc, zdc1); + const unsigned char *srow = bm->bytes + bm->border; + unsigned char *drow = bytes_data + border + dr*bytes_per_row; + for (int sr = 0; sr < bm->nrows; sr++) + { + if (dr>=0 && dr<nrows) + { + int dc = zdc; + int dc1 = zdc1; + for (int sc=0; sc < bm->ncolumns; sc++) + { + if (dc>=0 && dc<ncolumns) + drow[dc] += srow[sc]; + if (++dc1 >= subsample) + { + dc1 = 0; + dc += 1; + } + } + } + // next line in source + srow += bm->bytes_per_row; + // next line fraction in destination + if (++dr1 >= subsample) + { + dr1 = 0; + dr += 1; + drow += bytes_per_row; + } + } + } + else if (bm->rle) + { + if (!bytes_data) + uncompress(); + // Blit from rle + int dr, dr1, zdc, zdc1; + euclidian_ratio(yh+bm->nrows-1, subsample, dr, dr1); + euclidian_ratio(xh, subsample, zdc, zdc1); + const unsigned char *runs = bm->rle; + unsigned char *drow = bytes_data + border + dr*bytes_per_row; + int sr = bm->nrows -1; + int sc = 0; + char p = 0; + int dc = zdc; + int dc1 = zdc1; + while (sr >= 0) + { + int z = read_run(runs); + if (sc+z > bm->ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + int nc = sc + z; + + if (dr>=0 && dr<nrows) + while (z>0 && dc<ncolumns) + { + int zd = subsample - dc1; + if (zd > z) + zd = z; + if (p && dc>=0) + drow[dc] += zd; + z -= zd; + dc1 += zd; + if (dc1 >= subsample) + { + dc1 = 0; + dc += 1; + } + } + // next fractional row + sc = nc; + p = 1 - p; + if (sc >= bm->ncolumns) + { + sc = 0; + dc = zdc; + dc1 = zdc1; + p = 0; + sr -= 1; + if (--dr1 < 0) + { + dr1 = subsample - 1; + dr -= 1; + drow -= bytes_per_row; + } + } + } + } +} + + + +// ------ load bitmaps + + +unsigned int +GBitmap::read_integer(char &c, ByteStream &bs) +{ + unsigned int x = 0; + // eat blank before integer + while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#') + { + if (c=='#') + do { } while (bs.read(&c,1) && c!='\n' && c!='\r'); + c = 0; + bs.read(&c, 1); + } + // check integer + if (c<'0' || c>'9') + G_THROW( ERR_MSG("GBitmap.not_int") ); + // eat integer + while (c>='0' && c<='9') + { + x = x*10 + c - '0'; + c = 0; + bs.read(&c, 1); + } + return x; +} + + +void +GBitmap::read_pbm_text(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + { + char bit = 0; + bs.read(&bit,1); + while (bit==' ' || bit=='\t' || bit=='\r' || bit=='\n') + { + bit=0; + bs.read(&bit,1); + } + if (bit=='1') + row[c] = 1; + else if (bit=='0') + row[c] = 0; + else + G_THROW( ERR_MSG("GBitmap.bad_PBM") ); + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_pgm_text(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + char lookahead = '\n'; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + row[c] = grays - 1 - read_integer(lookahead, bs); + row -= bytes_per_row; + } +} + +void +GBitmap::read_pbm_raw(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + unsigned char acc = 0; + unsigned char mask = 0; + for (int c = 0; c<ncolumns; c++) + { + if (!mask) + { + bs.read(&acc, 1); + mask = (unsigned char)0x80; + } + if (acc & mask) + row[c] = 1; + else + row[c] = 0; + mask >>= 1; + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_pgm_raw(ByteStream &bs) +{ + unsigned char *row = bytes_data + border; + row += (nrows-1) * bytes_per_row; + for (int n = nrows-1; n>=0; n--) + { + for (int c = 0; c<ncolumns; c++) + { + unsigned char x; + bs.read((void*)&x, 1); + row[c] = grays - 1 - x; + } + row -= bytes_per_row; + } +} + +void +GBitmap::read_rle_raw(ByteStream &bs) +{ + // interpret runs data + unsigned char h; + unsigned char p = 0; + unsigned char *row = bytes_data + border; + int n = nrows - 1; + row += n * bytes_per_row; + int c = 0; + while (n >= 0) + { + bs.read(&h, 1); + int x = h; + if (x >= (int)RUNOVERFLOWVALUE) + { + bs.read(&h, 1); + x = h + ((x - (int)RUNOVERFLOWVALUE) << 8); + } + if (c+x > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync") ); + while (x-- > 0) + row[c++] = p; + p = 1 - p; + if (c >= ncolumns) + { + c = 0; + p = 0; + row -= bytes_per_row; + n -= 1; + } + } +} + + +// ------ save bitmaps + +void +GBitmap::save_pbm(ByteStream &bs, int raw) +{ + // check arguments + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_make_PBM") ); + GMonitorLock lock(monitor()); + // header + { + GUTF8String head; + head.format("P%c\n%d %d\n", (raw ? '4' : '1'), ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + } + // body + if(raw) + { + if(!rle) + compress(); + const unsigned char *runs=rle; + const unsigned char * const runs_end=rle+rlelength; + const int count=(ncolumns+7)>>3; + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,count); + while(runs<runs_end) + { + rle_get_bitmap(ncolumns,runs,buf,false); + bs.writall(buf,count); + } + }else + { + if (!bytes) + uncompress(); + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + unsigned char eol='\n'; + for (int c=0; c<ncolumns;) + { + unsigned char bit= (row[c] ? '1' : '0'); + bs.write((void*)&bit, 1); + c += 1; + if (c==ncolumns || (c&(int)RUNMSBMASK)==0) + bs.write((void*)&eol, 1); + } + // next row + row -= bytes_per_row; + n -= 1; + } + } +} + +void +GBitmap::save_pgm(ByteStream &bs, int raw) +{ + // checks + GMonitorLock lock(monitor()); + if (!bytes) + uncompress(); + // header + GUTF8String head; + head.format("P%c\n%d %d\n%d\n", (raw ? '5' : '2'), ncolumns, nrows, grays-1); + bs.writall((void*)(const char *)head, head.length()); + // body + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + if (raw) + { + for (int c=0; c<ncolumns; c++) + { + char x = grays - 1 - row[c]; + bs.write((void*)&x, 1); + } + } + else + { + unsigned char eol='\n'; + for (int c=0; c<ncolumns; ) + { + head.format("%d ", grays - 1 - row[c]); + bs.writall((void*)(const char *)head, head.length()); + c += 1; + if (c==ncolumns || (c&0x1f)==0) + bs.write((void*)&eol, 1); + } + } + row -= bytes_per_row; + n -= 1; + } +} + +void +GBitmap::save_rle(ByteStream &bs) +{ + // checks + if (ncolumns==0 || nrows==0) + G_THROW( ERR_MSG("GBitmap.not_init") ); + GMonitorLock lock(monitor()); + if (grays > 2) + G_THROW( ERR_MSG("GBitmap.cant_make_PBM") ); + // header + GUTF8String head; + head.format("R4\n%d %d\n", ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + // body + if (rle) + { + bs.writall((void*)rle, rlelength); + } + else + { + unsigned char *runs = 0; + GPBuffer<unsigned char> gruns(runs); + int size = encode(runs,gruns); + bs.writall((void*)runs, size); + } +} + + +// ------ runs + + +void +GBitmap::makerows( + int nrows, const int ncolumns, unsigned char *runs, unsigned char *rlerows[]) +{ + while (nrows-- > 0) + { + rlerows[nrows] = runs; + int c; + for(c=0;c<ncolumns;c+=GBitmap::read_run(runs)) + EMPTY_LOOP; + if (c > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync2") ); + } +} + + +void +GBitmap::rle_get_bitmap ( + const int ncolumns, + const unsigned char *&runs, + unsigned char *bitmap, + const bool invert ) +{ + const int obyte_def=invert?0xff:0; + const int obyte_ndef=invert?0:0xff; + int mask=0x80,obyte=0; + for(int c=ncolumns;c > 0 ;) + { + int x=read_run(runs); + c-=x; + while((x--)>0) + { + if(!(mask>>=1)) + { + *(bitmap++) = obyte^obyte_def; + obyte=0; + mask=0x80; + for(;x>=8;x-=8) + { + *(bitmap++)=obyte_def; + } + } + } + if(c>0) + { + int x=read_run(runs); + c-=x; + while((x--)>0) + { + obyte|=mask; + if(!(mask>>=1)) + { + *(bitmap++)=obyte^obyte_def; + obyte=0; + mask=0x80; + for(;(x>8);x-=8) + *(bitmap++)=obyte_ndef; + } + } + } + } + if(mask != 0x80) + { + *(bitmap++)=obyte^obyte_def; + } +} + +int +GBitmap::rle_get_bits(int rowno, unsigned char *bits) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + if (rowno<0 || rowno>=nrows) + return 0; + if (!rlerows) + { + const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows); + makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows)); + } + int n = 0; + int p = 0; + int c = 0; + unsigned char *runs = rlerows[rowno]; + while (c < ncolumns) + { + const int x=read_run(runs); + if ((c+=x)>ncolumns) + c = ncolumns; + while (n<c) + bits[n++] = p; + p = 1-p; + } + return n; +} + + +int +GBitmap::rle_get_runs(int rowno, int *rlens) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + if (rowno<0 || rowno>=nrows) + return 0; + if (!rlerows) + { + const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows); + makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows)); + } + int n = 0; + int d = 0; + int c = 0; + unsigned char *runs = rlerows[rowno]; + while (c < ncolumns) + { + const int x=read_run(runs); + if (n>0 && !x) + { + n--; + d = d-rlens[n]; + } + else + { + rlens[n++] = (c+=x)-d; + d = c; + } + } + return n; +} + + +int +GBitmap::rle_get_rect(GRect &rect) const +{ + GMonitorLock lock(monitor()); + if (!rle) + return 0; + int area = 0; + unsigned char *runs = rle; + rect.xmin = ncolumns; + rect.ymin = nrows; + rect.xmax = 0; + rect.ymax = 0; + int r = nrows; + while (--r >= 0) + { + int p = 0; + int c = 0; + int n = 0; + while (c < ncolumns) + { + const int x=read_run(runs); + if(x) + { + if (p) + { + if (c < rect.xmin) + rect.xmin = c; + if ((c += x) > rect.xmax) + rect.xmax = c-1; + n += x; + } + else + { + c += x; + } + } + p = 1-p; + } + area += n; + if (n) + { + rect.ymin = r; + if (r > rect.ymax) + rect.ymax = r; + } + } + if (area==0) + rect.clear(); + return area; +} + + + +// ------ helpers + +int +GBitmap::encode(unsigned char *&pruns,GPBuffer<unsigned char> &gpruns) const +{ + // uncompress rle information + if (nrows==0 || ncolumns==0) + { + gpruns.resize(0); + return 0; + } + if (!bytes) + { + unsigned char *runs; + GPBuffer<unsigned char> gruns(runs,rlelength); + memcpy((void*)runs, rle, rlelength); + gruns.swap(gpruns); + return rlelength; + } + gpruns.resize(0); + // create run array + int pos = 0; + int maxpos = 1024 + ncolumns + ncolumns; + unsigned char *runs; + GPBuffer<unsigned char> gruns(runs,maxpos); + // encode bitmap as rle + const unsigned char *row = bytes + border; + int n = nrows - 1; + row += n * bytes_per_row; + while (n >= 0) + { + if (maxpos < pos+ncolumns+ncolumns+2) + { + maxpos += 1024 + ncolumns + ncolumns; + gruns.resize(maxpos); + } + + unsigned char *runs_pos=runs+pos; + const unsigned char * const runs_pos_start=runs_pos; + append_line(runs_pos,row,ncolumns); + pos+=(size_t)runs_pos-(size_t)runs_pos_start; + row -= bytes_per_row; + n -= 1; + } + // return result + gruns.resize(pos); + gpruns.swap(gruns); + return pos; +} + +void +GBitmap::decode(unsigned char *runs) +{ + // initialize pixel array + if (nrows==0 || ncolumns==0) + G_THROW( ERR_MSG("GBitmap.not_init") ); + bytes_per_row = ncolumns + border; + if (runs==0) + G_THROW( ERR_MSG("GBitmap.null_arg") ); + int npixels = nrows * bytes_per_row + border; + if (!bytes_data) + { + gbytes_data.resize(npixels); + bytes = bytes_data; + } + gbytes_data.clear(); + gzerobuffer=zeroes(bytes_per_row + border); + // interpret runs data + int c, n; + unsigned char p = 0; + unsigned char *row = bytes_data + border; + n = nrows - 1; + row += n * bytes_per_row; + c = 0; + while (n >= 0) + { + int x = read_run(runs); + if (c+x > ncolumns) + G_THROW( ERR_MSG("GBitmap.lost_sync2") ); + while (x-- > 0) + row[c++] = p; + p = 1 - p; + if (c >= ncolumns) + { + c = 0; + p = 0; + row -= bytes_per_row; + n -= 1; + } + } + // Free rle data possibly attached to this bitmap + grle.resize(0); + grlerows.resize(0); + rlelength = 0; +#ifndef NDEBUG + check_border(); +#endif +} + +class GBitmap::ZeroBuffer : public GPEnabled +{ +public: + ZeroBuffer(const unsigned int zerosize); + unsigned char *zerobuffer; + GPBuffer<unsigned char> gzerobuffer; +}; + +GBitmap::ZeroBuffer::ZeroBuffer(const unsigned int zerosize) +: gzerobuffer(zerobuffer,zerosize) +{ + gzerobuffer.clear(); + GBitmap::zerobuffer=zerobuffer; + GBitmap::zerosize=zerosize; +} + +static const unsigned char static_zerobuffer[]= +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+32 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+64 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+96 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+128 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+160 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+192 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+234 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+256 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+288 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+320 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+352 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+384 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+416 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+448 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+480 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+512 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+544 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+576 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+608 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+640 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+672 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+704 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+736 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+768 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+800 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+832 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+864 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+896 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+928 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+960 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+992 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // 4096 + +int GBitmap::zerosize = sizeof(static_zerobuffer); +unsigned char *GBitmap::zerobuffer=const_cast<unsigned char *>(static_zerobuffer); + +GP<GBitmap::ZeroBuffer> +GBitmap::zeroes(int required) +{ + GMonitorLock lock(&monitors[0]); // any monitor would do + static GP<GBitmap::ZeroBuffer> gzerobuffer; + if (zerosize < required) + { + int z; + for(z=zerosize;z<required;z <<= 1) + EMPTY_LOOP; + z=(z+0xfff)&(~0xfff); + gzerobuffer=new GBitmap::ZeroBuffer((unsigned int)z); + } + return gzerobuffer; +} + + +// Fills a bitmap with the given value +void +GBitmap::fill(unsigned char value) +{ + GMonitorLock lock(monitor()); + for(unsigned int y=0; y<rows(); y++) + { + unsigned char* bm_y = (*this)[y]; + for(unsigned int x=0; x<columns(); x++) + bm_y[x] = value; + } +} + + +void +GBitmap::append_long_run(unsigned char *&data, int count) +{ + while (count > MAXRUNSIZE) + { + data[0] = data[1] = 0xff; + data[2] = 0; + data += 3; + count -= MAXRUNSIZE; + } + if (count < RUNOVERFLOWVALUE) + { + data[0] = count; + data += 1; + } + else + { + data[0] = (count>>8) + GBitmap::RUNOVERFLOWVALUE; + data[1] = (count & 0xff); + data += 2; + } +} + + +void +GBitmap::append_line(unsigned char *&data,const unsigned char *row, + const int rowlen,bool invert) +{ + const unsigned char *rowend=row+rowlen; + bool p=!invert; + while(row<rowend) + { + int count=0; + if ((p=!p)) + { + if(*row) + for(++count,++row;(row<rowend)&&*row;++count,++row) + EMPTY_LOOP; + } + else if(!*row) + { + for(++count,++row;(row<rowend)&&!*row;++count,++row) + EMPTY_LOOP; + } + append_run(data,count); + } +} + +#if 0 +static inline int +GetRowTDLRNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + stopptr=(startptr=bit[row])+bit.columns(); + return 1; +} + +static inline int +GetRowTDLRNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + stopptr=(startptr=bit[row])+bit.columns(); + return 1; +} + +static inline int +GetRowTDRLNR( + GBitmap &bit,const int row, const unsigned char *&startptr, + const unsigned char *&stopptr) +{ + startptr=(stopptr=bit[row]-1)+bit.columns(); + return -1; +} +#endif // 0 + +GP<GBitmap> +GBitmap::rotate(int count) +{ + GP<GBitmap> newbitmap=this; + if((count%=4)) + { + if( count & 0x01 ) + { + newbitmap = new GBitmap(ncolumns, nrows); + }else + { + newbitmap = new GBitmap(nrows, ncolumns); + } + GMonitorLock lock(monitor()); + if (!bytes_data) + uncompress(); + GBitmap &dbitmap = *newbitmap; + dbitmap.set_grays(grays); + switch(count) + { + case 1: // rotate 90 counter clockwise + { + const int lastrow = dbitmap.rows()-1; + for(int y=0; y<nrows; y++) + { + const unsigned char *r=operator[] (y); + for(int x=0,xnew=lastrow;xnew>=0; x++,xnew--) + { + dbitmap[xnew][y] = r[x]; + } + } + } + break; + case 2: // rotate 180 counter clockwise + { + const int lastrow = dbitmap.rows()-1; + const int lastcolumn = dbitmap.columns()-1; + for(int y=0,ynew=lastrow;ynew>=0; y++,ynew--) + { + const unsigned char *r=operator[] (y); + unsigned char *d=dbitmap[ynew]; + for(int xnew=lastcolumn;xnew>=0; r++,--xnew) + { + d[xnew] = *r; + } + } + } + break; + case 3: // rotate 270 counter clockwise + { + const int lastcolumn = dbitmap.columns()-1; + for(int y=0,ynew=lastcolumn;ynew>=0;y++,ynew--) + { + const unsigned char *r=operator[] (y); + for(int x=0; x<ncolumns; x++) + { + dbitmap[x][ynew] = r[x]; + } + } + } + break; + } + if(grays == 2) + { + compress(); + dbitmap.compress(); + } + } + return newbitmap; +} + +#ifndef NDEBUG +void +GBitmap::check_border() const +{int col ; + if (bytes) + { + const unsigned char *p = (*this)[-1]; + for (col=-border; col<ncolumns+border; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.zero_damaged") ); + for (int row=0; row<nrows; row++) + { + p = (*this)[row]; + for (col=-border; col<0; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.left_damaged") ); + for (col=ncolumns; col<ncolumns+border; col++) + if (p[col]) + G_THROW( ERR_MSG("GBitmap.right_damaged") ); + } + } +} +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GBitmap.h b/kviewshell/plugins/djvu/libdjvu/GBitmap.h new file mode 100644 index 00000000..74669c05 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GBitmap.h @@ -0,0 +1,673 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GBitmap.h,v 1.9 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GBITMAP_H_ +#define _GBITMAP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" +#ifndef NDEBUG +#include "GException.h" +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GRect; +class GMonitor; +class ByteStream; + +/** @name GBitmap.h + + Files #"GBitmap.h"# and #"GBitmap.cpp"# implement class \Ref{GBitmap}. + Instances of this class represent bilevel or gray-level images. The + ``bottom left'' coordinate system is used consistently in the DjVu library. + Line zero of a bitmap is the bottom line in the bitmap. Pixels are + organized from left to right within each line. As suggested by its name, + class #GBitmap# was initially a class for bilevel images only. It was + extended to handle gray-level images when arose the need to render + anti-aliased images. This class has been a misnomer since then. + + {\bf ToDo} --- Class #GBitmap# can internally represent bilevel images + using a run-length encoded representation. Some algorithms may benefit + from a direct access to this run information. + + @memo + Generic support for bilevel and gray-level images. + @author + L\'eon Bottou <[email protected]> + @version + #$Id: GBitmap.h,v 1.9 2004/04/17 23:56:11 leonb Exp $# + + */ +//@{ + + +/** Bilevel and gray-level images. Instances of class #GBitmap# represent + bilevel or gray-level images. Images are usually represented using one + byte per pixel. Value zero represents a white pixel. A value equal to + the number of gray levels minus one represents a black pixel. The number + of gray levels is returned by the function \Ref{get_grays} and can be + manipulated by the functions \Ref{set_grays} and \Ref{change_grays}. + + The bracket operator returns a pointer to the bytes composing one line of + the image. This pointer can be used to read or write the image pixels. + Line zero represents the bottom line of the image. + + The memory organization is setup in such a way that you can safely read a + few pixels located in a small border surrounding all four sides of the + image. The width of this border can be modified using the function + \Ref{minborder}. The border pixels are initialized to zero and therefore + represent white pixels. You should never write anything into border + pixels because they are shared between images and between lines. */ + +class GBitmap : public GPEnabled +{ +protected: + GBitmap(void); + GBitmap(int nrows, int ncolumns, int border=0); + GBitmap(const GBitmap &ref); + GBitmap(const GBitmap &ref, int border); + GBitmap(const GBitmap &ref, const GRect &rect, int border=0); + GBitmap(ByteStream &ref, int border=0); +public: + virtual ~GBitmap(); + void destroy(void); + /** @name Construction. */ + //@{ + /** Constructs an empty GBitmap object. The returned GBitmap has zero rows + and zero columns. Use function \Ref{init} to change the size of the + image. */ + static GP<GBitmap> create(void) {return new GBitmap;} + + /** Constructs a GBitmap with #nrows# rows and #ncolumns# columns. All + pixels are initialized to white. The optional argument #border# + specifies the size of the optional border of white pixels surrounding + the image. The number of gray levels is initially set to #2#. */ + static GP<GBitmap> create(const int nrows, const int ncolumns, const int border=0) + {return new GBitmap(nrows,ncolumns, border); } + + /** Copy constructor. Constructs a GBitmap by replicating the size, the + border and the contents of GBitmap #ref#. */ + static GP<GBitmap> create(const GBitmap &ref) + {return new GBitmap(ref);} + + /** Constructs a GBitmap by copying the contents of GBitmap #ref#. + Argument #border# specifies the width of the optional border. */ + static GP<GBitmap> create(const GBitmap &ref, const int border) + { return new GBitmap(ref,border); } + + /** Constructs a GBitmap by copying a rectangular segment #rect# of GBitmap + #ref#. The optional argument #border# specifies the size of the + optional border of white pixels surrounding the image. */ + static GP<GBitmap> create(const GBitmap &ref, const GRect &rect, const int border=0) + { return new GBitmap(ref,rect,border); } + + /** Constructs a GBitmap by reading PBM, PGM or RLE data from ByteStream + #ref# into this GBitmap. The optional argument #border# specifies the + size of the optional border of white pixels surrounding the image. See + \Ref{PNM and RLE file formats} for more information. */ + static GP<GBitmap> create(ByteStream &ref, const int border=0) + { return new GBitmap(ref,border); } + + //@} + + /** @name Initialization. */ + //@{ + /** Resets this GBitmap size to #nrows# rows and #ncolumns# columns and sets + all pixels to white. The optional argument #border# specifies the size + of the optional border of white pixels surrounding the image. The + number of gray levels is initialized to #2#. */ + void init(int nrows, int ncolumns, int border=0); + /** Initializes this GBitmap with the contents of the GBitmap #ref#. The + optional argument #border# specifies the size of the optional border of + white pixels surrounding the image. */ + void init(const GBitmap &ref, int border=0); + /** Initializes this GBitmap with a rectangular segment #rect# of GBitmap + #ref#. The optional argument #border# specifies the size of the + optional border of white pixels surrounding the image. */ + void init(const GBitmap &ref, const GRect &rect, int border=0); + /** Reads PBM, PGM or RLE data from ByteStream #ref# into this GBitmap. The + previous content of the GBitmap object is lost. The optional argument + #border# specifies the size of the optional border of white pixels + surrounding the image. See \Ref{PNM and RLE file formats} for more + information. */ + void init(ByteStream &ref, int border=0); + /** Assignment operator. Initializes this GBitmap by copying the size, the + border and the contents of GBitmap #ref#. */ + GBitmap& operator=(const GBitmap &ref); + /** Initializes all the GBitmap pixels to value #value#. */ + void fill(unsigned char value); + //@} + + /** @name Accessing the pixels. */ + //@{ + /** Returns the number of rows (the image height). */ + unsigned int rows() const; + /** Returns the number of columns (the image width). */ + unsigned int columns() const; + /** Returns a constant pointer to the first byte of row #row#. + This pointer can be used as an array to read the row elements. */ + const unsigned char *operator[] (int row) const; + /** Returns a pointer to the first byte of row #row#. + This pointer can be used as an array to read or write the row elements. */ + unsigned char *operator[] (int row); + /** Returns the size of a row in memory (in pixels). This number is equal + to the difference between pointers to pixels located in the same column + in consecutive rows. This difference can be larger than the number of + columns in the image. */ + unsigned int rowsize() const; + /** Makes sure that the border is at least #minimum# pixels large. This + function does nothing it the border width is already larger than + #minimum#. Otherwise it reorganizes the data in order to provide a + border of #minimum# pixels. */ + void minborder(int minimum); + //@} + + /** @name Managing gray levels. */ + //@{ + /** Returns the number of gray levels. + Value #2# denotes a bilevel image. */ + int get_grays() const; + /** Sets the number of gray levels without changing the pixels. + Argument #grays# must be in range #2# to #256#. */ + void set_grays(int grays); + /** Changes the number of gray levels. The argument #grays# must be in the + range #2# to #256#. All the pixel values are then rescaled and clipped + in range #0# to #grays-1#. */ + void change_grays(int grays); + /** Binarizes a gray level image using a threshold. The number of gray + levels is reduced to #2# as in a bilevel image. All pixels whose value + was strictly greater than #threshold# are set to black. All other pixels + are set to white. */ + void binarize_grays(int threshold=0); + //@} + + /** @name Optimizing the memory usage. + The amount of memory used by bilevel images can be reduced using + function \Ref{compress}, which encodes the image using a run-length + encoding scheme. The bracket operator decompresses the image on demand. + A few highly optimized functions (e.g. \Ref{blit}) can use a run-length + encoded bitmap without decompressing it. There are unfortunate locking + issues associated with this capability (c.f. \Ref{share} and + \Ref{monitor}). */ + //@{ + /** Reduces the memory required for a bilevel image by using a run-length + encoded representation. Functions that need to access the pixel array + will decompress the image on demand. */ + void compress(); + /** Decodes run-length encoded bitmaps and recreate the pixel array. + This function is usually called by #operator[]# when needed. */ + void uncompress(); + /** Returns the number of bytes allocated for this image. */ + unsigned int get_memory_usage() const; + /** Returns a possibly null pointer to a \Ref{GMonitor} for this bitmap. + You should use this monitor to ensure that the data representation of the + bitmap will not change while you are using it. We suggest using + class \Ref{GMonitorLock} which properly handles null monitor pointers. */ + GMonitor *monitor() const; + /** Associates a \Ref{GMonitor} with this bitmap. This function should be + called on all bitmaps susceptible of being simultaneously used by + several threads. It will make sure that function \Ref{monitor} returns + a pointer to a suitable monitor for this bitmap. */ + void share(); + //@} + + /** @name Accessing RLE data. + The next functions are useful for processing bilevel images + encoded using the run length encoding scheme. These functions always return + zero if the bitmap is not RLE encoded. Function \Ref{compress} must + be used to ensure that the bitmap is RLE encoded. */ + //@{ + /** Gets the pixels for line #rowno#. One line of pixel is stored as + #unsigned char# values into array #bits#. Each pixel is either 1 or 0. + The array must be large enough to hold the whole line. The number of + pixels is returned. */ + + int rle_get_bits(int rowno, unsigned char *bits) const; + + /** Gets the bitmap line rle data passed. One line of pixel is stored one + with 8 bits per #unsigned char# in an array. The array must be large + enough to hold the whole line. */ + + static void rle_get_bitmap(const int ncolumns,const unsigned char *&runs, + unsigned char *bitmap, const bool invert ); + + /** Gets the lengths of all runs in line #rowno#. The array #rlens# must be + large enough to accomodate #w+2# integers where #w# is the number of + columns in the image. These integers represent the lengths of + consecutive runs of alternatively white or black pixels. Lengths can be + zero in order to allow for lines starting with black pixels. This + function returns the total number of runs in the line. */ + int rle_get_runs(int rowno, int *rlens) const; + /** Gets the smallest rectangle enclosing black pixels. + Rectangle rect gives the coordinates of the smallest rectangle + containing all black pixels. Returns the number of black pixels. */ + int rle_get_rect(GRect &rect) const; + //@} + + /** @name Additive Blit. + The blit functions are designed to efficiently construct an anti-aliased + image by copying smaller images at predefined locations. The image of a + page, for instance, is composed by copying the images of characters at + predefined locations. These functions are fairly optimized. They can + directly use compressed GBitmaps (see \Ref{compress}). We consider in + this section that each GBitmap comes with a coordinate system defined as + follows. Position (#0#,#0#) corresponds to the bottom left corner of + the bottom left pixel. Position (#1#,#1#) corresponds to the top right + corner of the bottom left pixel, which is also the bottom left corner of + the second pixel of the second row. Position (#w#,#h#), where #w# and + #h# denote the size of the GBitmap, corresponds to the top right corner + of the top right pixel. */ + + //@{ + /** Performs an additive blit of the GBitmap #bm#. The GBitmap #bm# is + first positioned above the current GBitmap in such a way that position + (#u#,#v#) in GBitmap #bm# corresponds to position (#u#+#x#,#v#+#y#) in + the current GBitmap. The value of each pixel in GBitmap #bm# is then + added to the value of the corresponding pixel in the current GBitmap. + + {\bf Example}: Assume for instance that the current GBitmap is initially + white (all pixels have value zero). This operation copies the pixel + values of GBitmap #bm# at position (#x#,#y#) into the current GBitmap. + Note that function #blit# does not change the number of gray levels in + the current GBitmap. You may have to call \Ref{set_grays} to specify + how the pixel values should be interpreted. */ + void blit(const GBitmap *bm, int x, int y); + /** Performs an additive blit of the GBitmap #bm# with anti-aliasing. The + GBitmap #bm# is first positioned above the current GBitmap in such a + way that position (#u#,#v#) in GBitmap #bm# corresponds to position + (#u#+#x#/#subsample#,#v#+#y#/#subsample#) in the current GBitmap. This + mapping results in a contraction of GBitmap #bm# by a factor + #subsample#. Each pixel of the current GBitmap can be covered by a + maximum of #subsample^2# pixels of GBitmap #bm#. The value of + each pixel in GBitmap #bm# is then added to the value of the + corresponding pixel in the current GBitmap. + + {\bf Example}: Assume for instance that the current GBitmap is initially + white (all pixels have value zero). Each pixel of the current GBitmap + then contains the sum of the gray levels of the corresponding pixels in + GBitmap #bm#. There are up to #subsample*subsample# such pixels. If + for instance GBitmap #bm# is a bilevel image (pixels can be #0# or #1#), + the pixels of the current GBitmap can take values in range #0# to + #subsample*subsample#. Note that function #blit# does not change the + number of gray levels in the current GBitmap. You must call + \Ref{set_grays} to indicate that there are #subsample^2+1# gray + levels. Since there is at most 256 gray levels, this also means that + #subsample# should never be greater than #15#. + + {\bf Remark}: Arguments #x# and #y# do not represent a position in the + coordinate system of the current GBitmap. According to the above + discussion, the position is (#x/subsample#,#y/subsample#). In other + words, you can position the blit with a sub-pixel resolution. The + resulting anti-aliasing changes are paramount to the image quality. */ + void blit(const GBitmap *shape, int x, int y, int subsample); + //@} + + /** @name Saving images. + The following functions write PBM, PGM and RLE files. PBM and PGM are + well known formats for bilevel and gray-level images. The RLE is a + simple run-length encoding scheme for bilevel images. These files can be + read using the ByteStream based constructor or initialization function. + See \Ref{PNM and RLE file formats} for more information. */ + //@{ + /** Saves the image into ByteStream #bs# using the PBM format. Argument + #raw# selects the ``Raw PBM'' (1) or the ``Ascii PBM'' (0) format. The + image is saved as a bilevel image. All non zero pixels are considered + black pixels. See section \Ref{PNM and RLE file formats}. */ + void save_pbm(ByteStream &bs, int raw=1); + /** Saves the image into ByteStream #bs# using the PGM format. Argument + #raw# selects the ``Raw PGM'' (1) or the ``Ascii PGM'' (0) format. The + image is saved as a gray level image. See section + \Ref{PNM and RLE file formats}. */ + void save_pgm(ByteStream &bs, int raw=1); + /** Saves the image into ByteStream #bs# using the RLE file format. + The image is saved as a bilevel image. All non zero pixels are + considered black pixels. See section \Ref{PNM and RLE file formats}. */ + void save_rle(ByteStream &bs); + //@} + + /** @name Stealing or borrowing the memory buffer (advanced). */ + //@{ + /** Steals the memory buffer of a GBitmap. This function returns the + address of the memory buffer allocated by this GBitmap object. The + offset of the first pixel in the bottom line is written into variable + #offset#. Other lines can be accessed using pointer arithmetic (see + \Ref{rowsize}). The GBitmap object no longer ``owns'' the buffer: you + must explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GBitmap object. This function will return a + null pointer if the GBitmap object does not ``own'' the buffer in the + first place. */ + unsigned char *take_data(size_t &offset); + /** Initializes this GBitmap by borrowing a memory segment. The GBitmap + then directly addresses the memory buffer #data# provided by the user. + This buffer must be large enough to hold #w*h# bytes representing each + one pixel. The GBitmap object does not ``own'' the buffer: you must + explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GBitmap object. */ + inline void borrow_data(unsigned char &data, int w, int h); + /** Same as borrow_data, except GBitmap will call #delete[]#. */ + void donate_data(unsigned char *data, int w, int h); + /** Return a pointer to the rle data. */ + const unsigned char *get_rle(unsigned int &rle_length); + /** Initializes this GBitmap by setting the size to #h# rows and #w# + columns, and directly addressing the memory buffer #rledata# provided by + the user. This buffer contains #rledatalen# bytes representing the + bitmap in run length encoded form. The GBitmap object then ``owns'' the + buffer (unlike #borrow_data#, but like #donate_data#) and will + deallocate this buffer when appropriate: you should not deallocate this + buffer yourself. The encoding of buffer #rledata# is similar to the + data segment of the RLE file format (without the header) documented in + \Ref{PNM and RLE file formats}. */ + void donate_rle(unsigned char *rledata, unsigned int rledatalen, int w, int h); + /** Static function for parsing run data. + This function returns one run length encoded at position #data# + and increments the pointer #data# accordingly. */ + static inline int read_run(const unsigned char *&data); + static inline int read_run(unsigned char *&data); + /** Static function for generating run data. + This function encoded run length #count# at position #data# + and increments the pointer accordingly. The pointer must + initially point to a large enough data buffer. */ + static inline void append_run(unsigned char *&data, int count); + /** Rotates bitmap by 90, 180 or 270 degrees anticlockwise + and returns a new pixmap, input bitmap is not changed. + count can be 1, 2, or 3 for 90, 180, 270 degree rotation. + It returns the same bitmap if not rotated. + The input bitmap will be uncompressed for rotation*/ + GP<GBitmap> rotate(int count=0); + //@} + +// These are constants, but we use enum because that works on older compilers. + enum {MAXRUNSIZE=0x3fff}; + enum {RUNOVERFLOWVALUE=0xc0}; + enum {RUNMSBMASK=0x3f}; + enum {RUNLSBMASK=0xff}; + + +protected: + // bitmap components + unsigned short nrows; + unsigned short ncolumns; + unsigned short border; + unsigned short bytes_per_row; + unsigned short grays; + unsigned char *bytes; + unsigned char *bytes_data; + GPBuffer<unsigned char> gbytes_data; + unsigned char *rle; + GPBuffer<unsigned char> grle; + unsigned char **rlerows; + GPBuffer<unsigned char *> grlerows; + unsigned int rlelength; +private: + GMonitor *monitorptr; +public: + class ZeroBuffer; + friend class ZeroBuffer; + GP<ZeroBuffer> gzerobuffer; +private: + static int zerosize; + static unsigned char *zerobuffer; + static GP<ZeroBuffer> zeroes(int ncolumns); + static unsigned int read_integer(char &lookahead, ByteStream &ref); + static void euclidian_ratio(int a, int b, int &q, int &r); + int encode(unsigned char *&pruns,GPBuffer<unsigned char> &gpruns) const; + void decode(unsigned char *runs); + void read_pbm_text(ByteStream &ref); + void read_pgm_text(ByteStream &ref); + void read_pbm_raw(ByteStream &ref); + void read_pgm_raw(ByteStream &ref); + void read_rle_raw(ByteStream &ref); + static void append_long_run(unsigned char *&data, int count); + static void append_line(unsigned char *&data,const unsigned char *row, + const int rowlen,bool invert=false); + static void makerows(int,const int, unsigned char *, unsigned char *[]); + friend class DjVu_Stream; + friend class DjVu_PixImage; +public: +#ifndef NDEBUG + void check_border() const; +#endif +}; + + +/** @name PNM and RLE file formats + + {\bf PNM} --- There are actually three PNM file formats: PBM for bilevel + images, PGM for gray level images, and PPM for color images. These + formats are widely used by popular image manipulation packages such as + NetPBM \URL{http://www.arc.umn.edu/GVL/Software/netpbm.html} or + ImageMagick \URL{http://www.wizards.dupont.com/cristy/}. + + {\bf RLE} --- The binary RLE file format is a simple run-length encoding + scheme for storing bilevel images. Encoding or decoding a RLE encoded + file is extremely simple. Yet RLE encoded files are usually much smaller + than the corresponding PBM encoded files. RLE files always begin with a + header line composed of:\\ + - the two characters #"R4"#,\\ + - one or more blank characters,\\ + - the number of columns, encoded using characters #"0"# to #"9"#,\\ + - one or more blank characters,\\ + - the number of lines, encoded using characters #"0"# to #"9"#,\\ + - exactly one blank character (usually a line-feed character). + + The rest of the file encodes a sequence of numbers representing the + lengths of alternating runs of white and black pixels. Lines are encoded + starting with the top line and progressing towards the bottom line. Each + line starts with a white run. The decoder knows that a line is finished + when the sum of the run lengths for that line is equal to the number of + columns in the image. Numbers in range #0# to #191# are represented by a + single byte in range #0x00# to #0xbf#. Numbers in range #192# to #16383# + are represented by a two byte sequence: the first byte, in range #0xc0# to + #0xff#, encodes the six most significant bits of the number, the second + byte encodes the remaining eight bits of the number. This scheme allows + for runs of length zero, which are useful when a line starts with a black + pixel, and when a very long run (whose length exceeds #16383#) must be + split into smaller runs. + + @memo + Simple image file formats. */ + +//@} + + +// ---------------- IMPLEMENTATION + +inline unsigned int +GBitmap::rows() const +{ + return nrows; +} + +inline unsigned int +GBitmap::columns() const +{ + return ncolumns; +} + +inline unsigned int +GBitmap::rowsize() const +{ + return bytes_per_row; +} + +inline int +GBitmap::get_grays() const +{ + return grays; +} + +inline unsigned char * +GBitmap::operator[](int row) +{ + if (!bytes) uncompress(); + if (row<0 || row>=nrows) { +#ifndef NDEBUG + if (zerosize < bytes_per_row + border) + G_THROW( ERR_MSG("GBitmap.zero_small") ); +#endif + return zerobuffer + border; + } + return &bytes[row * bytes_per_row + border]; +} + +inline const unsigned char * +GBitmap::operator[](int row) const +{ + if (!bytes) ((GBitmap*)this)->uncompress(); + if (row<0 || row>=nrows) { +#ifndef NDEBUG + if (zerosize < bytes_per_row + border) + G_THROW( ERR_MSG("GBitmap.zero_small") ); +#endif + return zerobuffer + border; + } + return &bytes[row * bytes_per_row + border]; +} + +inline GBitmap& +GBitmap::operator=(const GBitmap &ref) +{ + init(ref, ref.border); + return *this; +} + +inline GMonitor * +GBitmap::monitor() const +{ + return monitorptr; +} + +inline void +GBitmap::euclidian_ratio(int a, int b, int &q, int &r) +{ + q = a / b; + r = a - b*q; + if (r < 0) + { + q -= 1; + r += b; + } +} + + +inline int +GBitmap::read_run(unsigned char *&data) +{ + register int z=*data++; + return (z>=RUNOVERFLOWVALUE)? + ((z&~RUNOVERFLOWVALUE)<<8)|(*data++):z; +} + +inline int +GBitmap::read_run(const unsigned char *&data) +{ + register int z=*data++; + return (z>=RUNOVERFLOWVALUE)? + ((z&~RUNOVERFLOWVALUE)<<8)|(*data++):z; +} + +inline void +GBitmap::append_run(unsigned char *&data, int count) +{ + if (count < RUNOVERFLOWVALUE) + { + data[0] = count; + data += 1; + } + else if (count <= MAXRUNSIZE) + { + data[0] = (count>>8) + GBitmap::RUNOVERFLOWVALUE; + data[1] = (count & 0xff); + data += 2; + } + else + { + append_long_run(data, count); + } +} + + +inline void +GBitmap::borrow_data(unsigned char &data,int w,int h) +{ + donate_data(&data,w,h); + bytes_data=0; +} + +// ---------------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GContainer.cpp b/kviewshell/plugins/djvu/libdjvu/GContainer.cpp new file mode 100644 index 00000000..2019439c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GContainer.cpp @@ -0,0 +1,802 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GContainer.cpp,v 1.12 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GContainer.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ------------------------------------------------------------ +// DYNAMIC ARRAYS +// ------------------------------------------------------------ + + +GArrayBase::GArrayBase(const GArrayBase &ref) + : traits(ref.traits), + gdata(data,0,1), + minlo(ref.minlo), maxhi(ref.maxhi), + lobound(ref.lobound), hibound(ref.hibound) +{ + if (maxhi >= minlo) + gdata.resize(traits.size * (maxhi - minlo + 1),1); + if (hibound >= lobound) + traits.copy(traits.lea(data, lobound-minlo), + traits.lea(ref.data, lobound-minlo), + hibound - lobound + 1, 0); +} + + +GArrayBase::GArrayBase(const GCONT Traits &traits) + : traits(traits), + gdata(data,0,1), + minlo(0), maxhi(-1), + lobound(0), hibound(-1) +{ +} + + +GArrayBase::GArrayBase(const GCONT Traits &traits, int lobound, int hibound) + : traits(traits), + gdata(data,0,1), + minlo(0), maxhi(-1), + lobound(0), hibound(-1) +{ + resize(lobound, hibound); +} + + +GArrayBase::~GArrayBase() +{ + G_TRY { empty(); } G_CATCH_ALL { } G_ENDCATCH; +} + + +GArrayBase & +GArrayBase::operator= (const GArrayBase &ga) +{ + if (this == &ga) + return *this; + empty(); + if (ga.hibound >= ga.lobound) + { + resize(ga.lobound, ga.hibound); + traits.copy( traits.lea(data, lobound-minlo), + traits.lea(ga.data, ga.lobound-ga.minlo), + hibound - lobound + 1, 0 ); + } + return *this; +} + + +void +GArrayBase::steal(GArrayBase &ga) +{ + if (this != &ga) + { + empty(); + lobound = ga.lobound; + hibound = ga.hibound; + minlo = ga.minlo; + maxhi = ga.maxhi; + data = ga.data; + ga.data = 0; + ga.lobound = ga.minlo = 0; + ga.hibound = ga.maxhi = -1; + } +} + + +void +GArrayBase::empty() +{ + resize(0, -1); +} + + +void +GArrayBase::touch(int n) +{ + int nlo = (n<lobound ? n : lobound); + int nhi = (n>hibound ? n : hibound); + if (hibound < lobound) + nlo = nhi = n; + resize(nlo, nhi); +} + + +void +GArrayBase::resize(int lo, int hi) +{ + // Validation + int nsize = hi - lo + 1; + if (nsize < 0) + G_THROW( ERR_MSG("GContainer.bad_args") ); + // Destruction + if (nsize == 0) + { + if (hibound >= lobound) + traits.fini( traits.lea(data, lobound-minlo), hibound-lobound+1 ); + if (data) + gdata.resize(0,1); + lobound = minlo = 0; + hibound = maxhi = -1; + return; + } + // Simple extension + if (lo >= minlo && hi <= maxhi) + { + if (lobound > lo) + traits.init( traits.lea(data,lo-minlo), lobound-lo ); + else if (lo > lobound) + traits.fini( traits.lea(data,lobound-minlo), lo-lobound ); + if (hi > hibound) + traits.init( traits.lea(data,hibound-minlo+1), hi-hibound ); + else if (hibound > hi) + traits.fini( traits.lea(data,hi-minlo+1), hibound-hi ); + lobound = lo; + hibound = hi; + return; + } + // General case + int nminlo = minlo; + int nmaxhi = maxhi; + if (nminlo > nmaxhi) + nminlo = nmaxhi = lo; + while (nminlo > lo) { + int incr = nmaxhi - nminlo; + nminlo -= (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + while (nmaxhi < hi) { + int incr = nmaxhi - nminlo; + nmaxhi += (incr < 8 ? 8 : (incr > 32768 ? 32768 : incr)); + } + // allocate and move + int beg = lo; + int end = hi; + int bytesize = traits.size * (nmaxhi-nminlo+1); + void *ndata; + GPBufferBase gndata(ndata,bytesize,1); +#if GCONTAINER_ZERO_FILL + memset(ndata, 0, bytesize); // slower but cleaner +#endif + if (lo < lobound) + { traits.init( traits.lea(ndata,lo-nminlo), lobound-lo ); beg=lobound; } + else if (lobound < lo) + { traits.fini( traits.lea(data,lobound-minlo), lo-lobound); } + if (hibound < hi) + { traits.init( traits.lea(ndata,hibound-nminlo+1), hi-hibound ); end=hibound; } + else if (hi < hibound) + { traits.fini( traits.lea(data, hi-minlo+1), hibound-hi ); } + if (end >= beg) + { traits.copy( traits.lea(ndata, beg-nminlo), + traits.lea(data, beg-minlo), + end-beg+1, 1 ); } + // free and replace + void *tmp=data; + data=ndata; + ndata=tmp; + minlo = nminlo; + maxhi = nmaxhi; + lobound = lo; + hibound = hi; +} + + +void +GArrayBase::shift(int disp) +{ + lobound += disp; + hibound += disp; + minlo += disp; + maxhi += disp; +} + + +void +GArrayBase::del(int n, int howmany) +{ + if (howmany < 0) + G_THROW( ERR_MSG("GContainer.bad_howmany") ); + if (howmany == 0) + return; + if ( n < lobound || n+(int)howmany-1 > hibound) + G_THROW( ERR_MSG("GContainer.bad_sub2") ); + traits.fini( traits.lea(data, n-minlo), howmany ); + if ( n+howmany-1 < hibound) + traits.copy( traits.lea(data, n-minlo), + traits.lea(data, n-minlo+howmany), + hibound - (n+howmany-1), 1 ); + hibound = hibound - howmany; +} + + +static inline void * +nextptr(void *p, int elsize) +{ + return (void*)(((char*)p) + elsize); +} + + +static inline void * +prevptr(void *p, int elsize) +{ + return (void*)(((char*)p) - elsize); +} + + +void +GArrayBase::ins(int n, const void *src, int howmany) +{ + if (howmany < 0) + G_THROW( ERR_MSG("GContainer.bad_howmany") ); + if (howmany == 0) + return; + // Make enough room + if (hibound+howmany > maxhi) + { + int nmaxhi = maxhi; + while (nmaxhi < hibound+howmany) + nmaxhi += (nmaxhi < 8 ? 8 : (nmaxhi > 32768 ? 32768 : nmaxhi)); + int bytesize = traits.size * (nmaxhi-minlo+1); + void *ndata; // = operator new (bytesize); + GPBufferBase gndata(ndata,bytesize,1); + memset(ndata, 0, bytesize); // slower but cleaner + if (hibound >= lobound) + traits.copy( traits.lea(ndata, lobound-minlo), + traits.lea(data, lobound-minlo), + hibound-lobound+1, 1 ); + maxhi = nmaxhi; + void *tmp=data; + data = ndata; + ndata=tmp; + } + // Shift data + int elsize = traits.size; + void *pdst = traits.lea(data, hibound+howmany-minlo); + void *psrc = traits.lea(data, hibound-minlo); + void *pend = traits.lea(data, n-minlo); + while ((char*)psrc >= (char*)pend) + { + traits.copy( pdst, psrc, 1, 1 ); + pdst = prevptr(pdst, elsize); + psrc = prevptr(psrc, elsize); + } + hibound += howmany; + // Initialize new data + if (! src) + { + traits.init( traits.lea(data, n-minlo), howmany ); + hibound += howmany; + return; + } + // Initialize new data with copy constructor + pdst = traits.lea(data, n-minlo); + pend = traits.lea(data, n+howmany-minlo); + while ((char*)pdst < (char*)pend) + { + traits.copy( pdst, src, 1, 0); + pdst = nextptr(pdst, elsize); + } +} + + + +// ------------------------------------------------------------ +// GPOSITION +// ------------------------------------------------------------ + + + +void +GPosition::throw_invalid(void *c) const +{ + if (c != cont) + G_THROW( ERR_MSG("GContainer.bad_pos_cont") ); + else if (! ptr) + G_THROW( ERR_MSG("GContainer.bad_pos_null") ); + else + G_THROW( ERR_MSG("GContainer.bad_pos") ); +} + + + +// ------------------------------------------------------------ +// DOUBLY LINKED LISTS +// ------------------------------------------------------------ + + +GListBase::GListBase(const Traits& traits) + : traits(traits) +{ + nelem = 0; + head.next = head.prev = 0; +} + + +GListBase::GListBase(const GListBase &ref) + : traits(ref.traits) +{ + nelem = 0; + head.next = head.prev = 0; + GListBase::operator= (ref); +} + +#include <stdio.h> +GListBase::~GListBase() +{ + G_TRY + { + empty(); + } + G_CATCH_ALL + { + } + G_ENDCATCH; +} + + +void +GListBase::append(Node *n) +{ + // Link + n->next = 0; + n->prev = head.prev; + head.prev = n; + if (n->prev) + n->prev->next = n; + else + head.next = n; + // Finish + nelem += 1; +} + + +void +GListBase::prepend(Node *n) +{ + // Link + n->next = head.next; + n->prev = 0; + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_after(GPosition pos, Node *n) +{ + // Prepare + if (pos.ptr) + { + if (pos.cont != (void*)this) + pos.throw_invalid((void*)this); + Node *p = pos.ptr; + n->prev = p; + n->next = p->next; + } + else + { + n->prev = 0; + n->next = head.next; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_before(GPosition pos, Node *n) +{ + // Prepare + if (pos.ptr) + { + if (pos.cont != (void*)this) + pos.throw_invalid((void*)this); + Node *p = pos.ptr; + n->prev = p->prev; + n->next = p; + } + else + { + n->prev = head.prev; + n->next = 0; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + // Finish + nelem += 1; +} + + +void +GListBase::insert_before(GPosition pos, GListBase &fromlist, GPosition &frompos) +{ + // Check + if (!frompos.ptr || frompos.cont != (void*)&fromlist) + frompos.throw_invalid((void*)&fromlist); + if (pos.ptr && pos.cont != (void*)this) + pos.throw_invalid((void*)this); + // Update frompos + Node *n = frompos.ptr; + frompos.ptr = n->next; + if (pos.ptr == n) return; + // Unlink + if (n->next) + n->next->prev = n->prev; + else + fromlist.head.prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + fromlist.head.next = n->next; + fromlist.nelem -= 1; + // Prepare insertion + if (pos.ptr) + { + Node *p = pos.ptr; + n->prev = p->prev; + n->next = p; + } + else + { + n->prev = head.prev; + n->next = 0; + } + // Link + if (n->prev) + n->prev->next = n; + else + head.next = n; + if (n->next) + n->next->prev = n; + else + head.prev = n; + nelem += 1; +} + + +void +GListBase::del(GPosition &pos) +{ + // Check + if (!pos.ptr || pos.cont != (void*)this) return; + // Unlink + Node *n = pos.ptr; + if (n->next) + n->next->prev = n->prev; + else + head.prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + head.next = n->next; + // Finish + nelem -= 1; + traits.fini( (void*)n, 1); + operator delete ( (void*)n ); + pos.ptr = 0; +} + + +GPosition +GListBase::nth(unsigned int n) const +{ + Node *p = 0; + if ((int)n < nelem) + for (p=head.next; p; p=p->next) + if ( n-- == 0) + break; + return GPosition(p, (void*)this); +} + + +void +GListBase::empty() +{ + Node *n=head.next; + while (n) + { + Node *p = n->next; + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + n = p; + } + head.next = head.prev = 0; + nelem = 0; +} + + +GListBase & +GListBase::operator= (const GListBase & ref) +{ + if (this == &ref) + return *this; + empty(); + for(Node *n = ref.head.next; n; n=n->next) + { + Node *m = (Node*) operator new (traits.size); + traits.copy( (void*)m, (void*)n, 1, 0); + append(m); + } + return *this; +} + + + + + +// ------------------------------------------------------------ +// ASSOCIATIVE MAPS +// ------------------------------------------------------------ + + + + +GSetBase::GSetBase(const Traits &traits) + : traits(traits), nelems(0), nbuckets(0), + gtable(table), first(0) +{ + rehash(17); +} + + +GSetBase::GSetBase(const GSetBase &ref) + : traits(ref.traits), + nelems(0), nbuckets(0), gtable(table), first(0) +{ + GSetBase::operator= (ref); +} + + +GSetBase::~GSetBase() +{ + G_TRY { empty(); } G_CATCH_ALL { } G_ENDCATCH; +// delete [] table; +} + + +GCONT HNode * +GSetBase::hashnode(unsigned int hashcode) const +{ + int bucket = hashcode % nbuckets; + return table[bucket]; +} + +GCONT HNode * +GSetBase::installnode(HNode *n) +{ + // Rehash if table is more than 60% full + if (nelems*3 > nbuckets*2) + rehash( 2*nbuckets - 1 ); + // Create and insert + insertnode(n); + return n; +} + +void +GSetBase::insertnode(HNode *n) +{ + int bucket = n->hashcode % nbuckets; + n->prev = n->hprev = table[bucket]; + if (n->prev) + { + // bucket was not empty + n->next = n->prev->next; + n->prev->next = n; + if (n->next) + n->next->prev = n; + } + else + { + // bucket was empty. + n->next = first; + first = n; + if (n->next) + n->next->prev = n; + } + // finish + table[bucket] = n; + nelems += 1; +} + + +void +GSetBase::deletenode(GCONT HNode *n) +{ + if (n == 0) + return; + int bucket = n->hashcode % nbuckets; + // Regular links + if (n->next) + n->next->prev = n->prev; + if (n->prev) + n->prev->next = n->next; + else + first = (HNode*)(n->next); + // HPrev links + if (table[bucket] == n) + table[bucket] = n->hprev; + else + ((HNode*)(n->next))->hprev = n->hprev; + // Delete entry + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + nelems -= 1; +} + + +void +GSetBase::rehash(int newbuckets) +{ + // Save chain of nodes + Node *n = first; + // Simulate an empty map + nelems = 0; + first = 0; + // Allocate a new empty bucket table +// delete [] table; + gtable.resize(0); + nbuckets = newbuckets; + typedef HNode *HNodePtr; +// table = new HNodePtr[nbuckets]; + gtable.resize(nbuckets); + gtable.clear(); +// for (int i=0; i<nbuckets; i++) +// table[i] = 0; + // Insert saved nodes + while (n) + { + Node *p = n->next; + insertnode((HNode*)n); + n = p; + } +} + + +GSetBase& +GSetBase::operator=(const GSetBase &ref) +{ + if (this == &ref) + return *this; + empty(); + rehash(ref.nbuckets); + for (Node *n = ref.first; n; n=n->next) + { + HNode *m = (HNode*) operator new (traits.size); + traits.copy( (void*)m, (void*)n, 1, 0); + insertnode(m); + } + return *this; +} + + +GPosition +GSetBase::firstpos() const +{ + return GPosition(first, (void*)this); +} + + +void +GSetBase::del(GPosition &pos) +{ + if (pos.ptr && pos.cont==(void*)this) + { + deletenode((HNode*)pos.ptr); + pos.ptr = 0; + } +} + +void +GSetBase::empty() +{ + HNode *n = first; + while (n) + { + HNode *p = (HNode*)(n->next); + traits.fini( (void*)n, 1 ); + operator delete ( (void*)n ); + n = p; + } + first = 0; + nelems = 0; + gtable.clear(); +// for (int i=0; i<nbuckets; i++) +// table[i] = 0; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GContainer.h b/kviewshell/plugins/djvu/libdjvu/GContainer.h new file mode 100644 index 00000000..d21838dc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GContainer.h @@ -0,0 +1,1366 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GContainer.h,v 1.15 2004/05/13 15:16:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GCONTAINER_H_ +#define _GCONTAINER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GException.h" +#include "GSmartPointer.h" +#include <string.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// Supports old iterators (first/last/next/prev) on lists and maps? +#ifndef GCONTAINER_OLD_ITERATORS +#define GCONTAINER_OLD_ITERATORS 1 +#endif + +// Check array bounds at runtime ? +#ifndef GCONTAINER_BOUNDS_CHECK +#define GCONTAINER_BOUNDS_CHECK 1 +#endif + +// Clears allocated memory prior to running constructors ? +#ifndef GCONTAINER_ZERO_FILL +#define GCONTAINER_ZERO_FILL 1 +#endif + +// Avoid member templates (needed by old compilers) +#ifndef GCONTAINER_NO_MEMBER_TEMPLATES +#if defined(__GNUC__) && (__GNUC__==2) && (__GNUC_MINOR__<91) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#elif defined(_MSC_VER) && !defined(__ICL) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#elif defined(__MWERKS__) +#define GCONTAINER_NO_MEMBER_TEMPLATES 1 +#else +#define GCONTAINER_NO_MEMBER_TEMPLATES 0 +#endif +#endif + +// Define typename when needed +#ifndef GCONTAINER_NO_TYPENAME +#define GCONTAINER_NO_TYPENAME 0 +#endif +#if GCONTAINER_NO_TYPENAME +#define typename /**/ +#endif + + +/** @name GContainer.h + + Files #"GContainer.h"# and #"GContainer.cpp"# implement three main + template class for generic containers. + Class #GArray# (see \Ref{Dynamic Arrays}) implements an array of objects + with variable bounds. Class #GList# (see \Ref{Doubly Linked Lists}) + implements a doubly linked list of objects. Class #GMap# (see + \Ref{Associative Maps}) implements a hashed associative map. The + container templates are not thread-safe. Thread safety can be implemented + using the facilities provided in \Ref{GThreads.h}. + + @memo + Template class for generic containers. + @author + L\'eon Bottou <[email protected]> -- initial implementation.\\ + Andrei Erofeev <[email protected]> -- bug fixes. + @version + #$Id: GContainer.h,v 1.15 2004/05/13 15:16:34 leonb Exp $# */ +//@{ + + + +// ------------------------------------------------------------ +// HELPER CLASSES +// ------------------------------------------------------------ + + + +/* Namespace for containers support classes. This class is used as a + namespace for global identifiers related to the implementation of + containers. It is inherited by all container objects. This is disabled by + defining compilation symbol #GCONTAINER_NO_MEMBER_TEMPATES# to 1. */ + + +#ifdef _MSC_VER +// Language lawyer say MS is wrong on that one. +// Cf section 5.4.7 in november 1997 draft. +#pragma warning( disable : 4243 ) +#endif + + +// GPEnabled inhertenced removed again so the code works on more machines. +class GCont +#if GCONTAINER_NO_MEMBER_TEMPLATES +{ +}; +#else +{ +public: +#endif + // --- Pointers to type management functions + struct Traits + { + int size; + void *(*lea) (void *base, int n); + void (*init) (void *dst, int n); + void (*copy) (void *dst, const void* src, int n, int zap); + void (*fini) (void *dst, int n); + }; +#if !GCONTAINER_NO_MEMBER_TEMPLATES +protected: +#endif + // --- Management of simple types + template <int SZ> class TrivTraits + { + public: + // The unique object + static const Traits & traits(); + // Offset in an array of T + static void * lea(void* base, int n) + { return (void*)( ((char*)base) + SZ*n ); } + // Trivial default constructor + static void init(void* dst, int n) {} + // Trivial copy constructor + static void copy(void* dst, const void* src, int n, int ) + { memcpy(dst, src, SZ*n); } + // Trivial destructor + static void fini(void* dst, int n) {} + }; + // --- Management of regular types + template <class T> class NormTraits + { + public: + // The unique object + static const Traits & traits(); + // Offset in an array of T + static void * lea(void* base, int n) + { return (void*)( ((T*)base) + n ); } + // Template based default constructor + static void init(void* dst, int n) + { T* d = (T*)dst; while (--n>=0) { new ((void*)d) T; d++; } } + // Template based copy constructor + static void copy(void* dst, const void* src, int n, int zap) + { T* d = (T*)dst; const T *s = (const T*)src; + while (--n>=0) { new ((void*)d) T(*s); if (zap) { s->T::~T(); }; d++; s++; } } + // Template based destructor + static void fini(void* dst, int n) + { T* d = (T*)dst; while (--n>=0) { d->T::~T(); d++; } } + }; + // --- Base class for list nodes + struct Node + { + Node *next; + Node *prev; + }; + // -- Class for list nodes + template <class T> struct ListNode : public Node + { + T val; + }; + // -- Class for map nodes showing the hash + struct HNode : public Node + { + HNode *hprev; + unsigned int hashcode; + }; + // -- Class for map nodes showing the hash and the key + template <class K> struct SetNode : public HNode + { + K key; + }; + // -- Class for map nodes with everything + template <class K, class T> struct MapNode : public SetNode<K> + { + T val; + }; +#if !GCONTAINER_NO_MEMBER_TEMPLATES +}; +#endif + + +#if !GCONTAINER_NO_MEMBER_TEMPLATES +#define GCONT GCont:: +#else +#define GCONT +#endif + +template <int SZ> const GCONT Traits & +GCONT TrivTraits<SZ>::traits() +{ + static const Traits theTraits = { + SZ, + TrivTraits<SZ>::lea, + TrivTraits<SZ>::init, + TrivTraits<SZ>::copy, + TrivTraits<SZ>::fini + }; + return theTraits; +} + +template <class T> const GCONT Traits & +GCONT NormTraits<T>::traits() +{ + static const Traits theTraits = { + sizeof(T), + NormTraits<T>::lea, + NormTraits<T>::init, + NormTraits<T>::copy, + NormTraits<T>::fini + }; + return theTraits; +} + + +// ------------------------------------------------------------ +// DYNAMIC ARRAYS +// ------------------------------------------------------------ + + +/** @name Dynamic Arrays + + These class implement arrays of objects of any type. Each element is + identified by an integer subscript. The valid subscripts range is defined + by dynamically adjustable lower- and upper-bounds. Besides accessing and + setting elements, member functions are provided to insert or delete + elements at specified positions. + + Class \Ref{GArrayTemplate} implements all methods for manipulating arrays + of type #TYPE#. You should not however create instances of this class. + You should instead use one of the following classes: + \begin{itemize} + \item Class \Ref{GArray<TYPE>} is the most general class, + \item Class \Ref{GTArray<TYPE>} is more efficient, but only works for + types that do not require sophisticated constructors or destructors, + such as the plain old C types (e.g. #int# or #char# ...). + \item Class \Ref{GPArray<TYPE>} implements an array of smart-pointers + \Ref{GP<TYPE>} to objects of type #TYPE#. Using this class + reduces the size of the code generated by the template instanciation. + \end{itemize} + + Another variant of dynamic arrays is implemented in file \Ref{Arrays.h}. + The main difference is that class \Ref{TArray}, \Ref{DArray} and + \Ref{DPArray} implement a copy-on-demand scheme. + + @memo Dynamic arrays. */ +//@{ + +class GArrayBase : public GCont +{ +public: + // -- CONSTRUCTORS + GArrayBase(const GArrayBase &ref); + GArrayBase(const Traits &traits); + GArrayBase(const Traits &traits, int lobound, int hibound); + // -- DESTRUCTOR + ~GArrayBase(); + // -- ASSIGNMENT + GArrayBase& operator= (const GArrayBase &ga); + // -- ALTERATION + void empty(); + void touch(int n); + void resize(int lobound, int hibound); + void shift(int disp); + void del(int n, int howmany=1); + void ins(int n, const void *src, int howmany=1); + void steal(GArrayBase &ga); +protected: + const Traits &traits; + void *data; + GPBufferBase gdata; + int minlo; + int maxhi; + int lobound; + int hibound; +}; + + +/** Common base class for all dynamic arrays. + Class \Ref{GArrayTemplate} implements all methods for manipulating arrays + of type #TYPE#. You should not however create instances of this class. + You should instead use class \Ref{GArray}, \Ref{GTArray} or + \Ref{GPArray}. */ + +template<class TYPE> +class GArrayTemplate : protected GArrayBase +{ +public: + // -- CONSTRUCTORS + GArrayTemplate(const Traits &traits) : GArrayBase(traits) {} + GArrayTemplate(const Traits &traits, int lobound, int hibound) + : GArrayBase(traits, lobound, hibound) {} + // -- ACCESS + /** Returns the number of elements in the array. */ + int size() const + { return hibound-lobound+1; } + /** Returns the lower bound of the valid subscript range. */ + int lbound() const + { return lobound; } + /** Returns the upper bound of the valid subscript range. */ + int hbound() const + { return hibound; } + /** Returns a reference to the array element for subscript #n#. This + reference can be used for both reading (as "#a[n]#") and writing (as + "#a[n]=v#") an array element. This operation will not extend the valid + subscript range: an exception \Ref{GException} is thrown if argument #n# + is not in the valid subscript range. */ + inline TYPE& operator[](int const n); + /** Returns a constant reference to the array element for subscript #n#. + This reference can only be used for reading (as "#a[n]#") an array + element. This operation will not extend the valid subscript range: an + exception \Ref{GException} is thrown if argument #n# is not in the valid + subscript range. This variant of #operator[]# is necessary when dealing + with a #const GArray<TYPE>#. */ + inline const TYPE& operator[](int n) const; + // -- CONVERSION + /** Returns a pointer for reading or writing the array elements. This + pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator TYPE* () + { return ((TYPE*)data)-minlo; } + /** Returns a pointer for reading (but not modifying) the array elements. + This pointer can be used to access the array elements with the same + subscripts and the usual bracket syntax. This pointer remains valid as + long as the valid subscript range is unchanged. If you change the + subscript range, you must stop using the pointers returned by prior + invocation of this conversion operator. */ + operator const TYPE* () const + { return ((const TYPE*)data)-minlo; } + operator const TYPE* () // suppress warning with gcc-2.95 + { return ((const TYPE*)data)-minlo; } + // -- ALTERATION + /** Erases the array contents. All elements in the array are destroyed. + The valid subscript range is set to the empty range. */ + void empty() + { GArrayBase::empty(); } + /** Extends the subscript range so that it contains #n#. + This function does nothing if #n# is already int the valid subscript range. + If the valid range was empty, both the lower bound and the upper bound + are set to #n#. Otherwise the valid subscript range is extended + to encompass #n#. This function is very handy when called before setting + an array element: + \begin{verbatim} + int lineno=1; + GArray<GString> a; + while (! end_of_file()) { + a.touch(lineno); + a[lineno++] = read_a_line(); + } + \end{verbatim} */ + void touch(int n) + { if (n<lobound || n>hibound) GArrayBase::touch(n); } + /** Resets the valid subscript range to #0#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #hibound# to + #-1# resets the valid subscript range to the empty range. */ + void resize(int hibound) + { GArrayBase::resize(0, hibound); } + /** Resets the valid subscript range to #lobound#---#hibound#. + This function may destroy some array elements and may construct + new array elements with the null constructor. Setting #lobound# to #0# and + #hibound# to #-1# resets the valid subscript range to the empty range. */ + void resize(int lobound, int hibound) + { GArrayBase::resize(lobound, hibound); } + /** Shifts the valid subscript range. Argument #disp# is added to both + bounds of the valid subscript range. Array elements previously + located at subscript #x# will now be located at subscript #x+disp#. */ + void shift(int disp) + { GArrayBase::shift(disp); } + /** Deletes array elements. The array elements corresponding to + subscripts #n#...#n+howmany-1# are destroyed. All array elements + previously located at subscripts greater or equal to #n+howmany# + are moved to subscripts starting with #n#. The new subscript upper + bound is reduced in order to account for this shift. */ + void del(int n, int howmany=1) + { GArrayBase::del(n, howmany); } + /** Insert new elements into an array. This function inserts + #howmany# elements at position #n# into the array. These + elements are constructed using the default constructor for type + #TYPE#. All array elements previously located at subscripts #n# + and higher are moved to subscripts #n+howmany# and higher. The + upper bound of the valid subscript range is increased in order + to account for this shift. */ + void ins(int n, int howmany=1) + { GArrayBase::ins(n, 0, howmany); } + /** Insert new elements into an array. The new elements are + constructed by copying element #val# using the copy constructor + for type #TYPE#. See \Ref{ins(int n, unsigned int howmany=1)}. */ + void ins(int n, const TYPE &val, int howmany=1) + { GArrayBase::ins(n, (const void*)&val, howmany); } + /** Steals contents from array #ga#. After this call, array #ga# is empty, + and this array contains everything previously contained in #ga#. */ + void steal(GArrayTemplate &ga) + { GArrayBase::steal(ga); } + // -- SORTING + /** Sort array elements. Sort all array elements in ascending + order according to the less-or-equal comparison + operator for type #TYPE#. */ + void sort() + { sort(lbound(), hbound()); } + /** Sort array elements in subscript range #lo# to #hi#. Sort all array + elements whose subscripts are in range #lo# to #hi# in ascending order + according to the less-or-equal comparison operator for type #TYPE#. The + other elements of the array are left untouched. An exception is thrown + if arguments #lo# and #hi# are not in the valid subscript range. */ + void sort(int lo, int hi); +}; + + + +/* That one must be implemented as a regular template function. */ +template <class TYPE> void +GArrayTemplate<TYPE>::sort(int lo, int hi) +{ + if (hi <= lo) + return; + if (hi > hibound || lo<lobound) + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + TYPE *data = (TYPE*)(*this); + // Test for insertion sort + if (hi <= lo + 50) + { + for (int i=lo+1; i<=hi; i++) + { + int j = i; + TYPE tmp = data[i]; + while ((--j>=lo) && !(data[j]<=tmp)) + data[j+1] = data[j]; + data[j+1] = tmp; + } + return; + } + // -- determine suitable quick-sort pivot + TYPE tmp = data[lo]; + TYPE pivot = data[(lo+hi)/2]; + if (pivot <= tmp) + { tmp = pivot; pivot=data[lo]; } + if (data[hi] <= tmp) + { pivot = tmp; } + else if (data[hi] <= pivot) + { pivot = data[hi]; } + // -- partition set + int h = hi; + int l = lo; + while (l < h) + { + while (! (pivot <= data[l])) l++; + while (! (data[h] <= pivot)) h--; + if (l < h) + { + tmp = data[l]; + data[l] = data[h]; + data[h] = tmp; + l = l+1; + h = h-1; + } + } + // -- recursively restart + sort(lo, h); + sort(l, hi); +} + +template<class TYPE> inline TYPE& +GArrayTemplate<TYPE>::operator[](int const n) +{ +#if GCONTAINER_BOUNDS_CHECK + if (n<lobound || n>hibound) + { + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + } +#endif + return ((TYPE*)data)[n-minlo]; +} + + +template<class TYPE> inline const TYPE & +GArrayTemplate<TYPE>::operator[](int const n) const +{ +#if GCONTAINER_BOUNDS_CHECK + if (n<lobound || n>hibound) + { + G_THROW( ERR_MSG("GContainer.illegal_subscript") ); + } +#endif + return ((const TYPE*)data)[n-minlo]; +} + + + +/** Dynamic array for general types. + Template class #GArray<TYPE># implements an array of elements of type + #TYPE#. This template class must be able to access the following + functions. + \begin{itemize} + \item a default constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item and optionally a destructor #TYPE::~TYPE()#. + \end{itemize} + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ + +template<class TYPE> +class GArray : public GArrayTemplate<TYPE> +{ +public: + /** Constructs an empty array. The valid subscript range is initially + empty. Member function #touch# and #resize# provide convenient ways + to enlarge the subscript range. */ + GArray() + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits() ) {} + /** Constructs an array with subscripts in range 0 to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. */ + GArray(int hi) + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits(), 0, hi ) {} + /** Constructs an array with subscripts in range #lobound# to #hibound#. + The subscript range can be subsequently modified with member functions + #touch# and #resize#. */ + GArray(int lo, int hi) + : GArrayTemplate<TYPE>(GCONT NormTraits<TYPE>::traits(), lo, hi ) {} + // Copy operator + GArray& operator=(const GArray &r) + { GArrayBase::operator=(r); return *this; } +}; + + +/** Dynamic array for smart pointers. + Template class #GPArray<TYPE># implements an array of elements of type + #GP<TYPE># (see \Ref{GSmartPointer.h}). Significantly smaller code sizes + can be achieved by using this class instead of the more general + #GArray<GP<TYPE>>#. + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ + +template<class TYPE> +class GPArray : public GArrayTemplate<GP<TYPE> > +{ +public: + GPArray() + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits() ) {} + GPArray(int hi) + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits(), 0, hi ) {} + GPArray(int lo, int hi) + : GArrayTemplate<GP<TYPE> >(GCONT NormTraits<GPBase>::traits(), lo, hi ) {} + // Copy operator + GPArray& operator=(const GPArray &r) + { GArrayBase::operator=(r); return *this; } +}; + +/** Dynamic array for simple types. + Template class #GTArray<TYPE># implements an array of elements of {\em + simple} type #TYPE#. {\em Simple} means that objects of type #TYPE# can + be created, copied, moved or destroyed without using specific constructors + or destructor functions. Class #GTArray<TYPE># will move or copy objects + using simple bitwise copies. Otherwise you must use class #GArray<TYPE>#. + This class only implement constructors. See class \Ref{GArrayTemplate} + for a description of all access methods. */ +template<class TYPE> +class GTArray : public GArrayTemplate<TYPE> +{ +public: + GTArray() + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits() ) {} + GTArray(int hi) + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits(), 0, hi ) {} + GTArray(int lo, int hi) + : GArrayTemplate<TYPE>(GCONT TrivTraits<sizeof(TYPE)>::traits(), lo, hi ) {} + // Copy operator + GTArray& operator=(const GTArray &r) + { GArrayBase::operator=(r); return *this; } +}; + + +//@} + + + +// ------------------------------------------------------------ +// DOUBLY LINKED LISTS +// ------------------------------------------------------------ + + +/** @name Doubly Linked Lists + + The template classes \Ref{GList} and \Ref{GPList} implement a doubly + linked list of objects of arbitrary types. Member functions are provided + to search the list for an element, to insert or delete elements at + specified positions. Theses template class must be able to access + \begin{itemize} + \item a default constructor #TYPE::TYPE()#, + \item a copy constructor #TYPE::TYPE(const TYPE &)#, + \item optionally a destructor #TYPE::~TYPE()#, + \item and optionally a comparison operator #TYPE::operator==(const TYPE &)#. + \end{itemize} + @memo Doubly linked lists. +*/ +//@{ + +/** Generic iterator class. + This class represents a position in a list (see \Ref{GList}) or a map + (see \Ref{GMap}). As demonstrated by the following examples, + this class should be used to iterate over the objects contained + in a list or a map: + \begin{verbatim} + void print_list(GList<GString> a) + { + for (GPosition i = a ; i; ++i) + DjVuPrintMessage("%s\n", (const char*) a[i] ); + } + + void print_list_backwards(GList<GString> a) + { + for (GPosition i = a.lastpos() ; i; --i) + DjVuPrintMessage("%s\n", (const char*) a[i] ); + } + \end{verbatim} + GPosition objects should only be used with the list or map for which they + have been created (using the member functions #firstpos# or #lastpos# of + the container). Furthermore, you should never use a GPosition object + which designates a list element which has been removed from the list + (using member function #del# or by other means.) +*/ + +class GPosition : protected GCont +{ +public: + /** Creates a null GPosition object. */ + GPosition() : ptr(0), cont(0) {} + /** Creates a copy of a GPosition object. */ + GPosition(const GPosition &ref) : ptr(ref.ptr), cont(ref.cont) {} + /** Tests whether this GPosition object is non null. */ + operator int() const + { return !!ptr; } + /** Tests whether this GPosition object is null. */ + int operator !() const + { return !ptr; } + /** Moves this GPosition object to the next object in the container. */ + GPosition& operator ++() + { if (ptr) ptr = ptr->next; return *this; } + /** Moves this GPosition object to the previous object in the container. */ + GPosition& operator --() + { if (ptr) ptr = ptr->prev; return *this; } + // Internal. Do not use. + GPosition(Node *p, void *c) : ptr(p), cont(c) {} +#if GCONTAINER_BOUNDS_CHECK + Node *check(void *c) + { if (!ptr || c!=cont) throw_invalid(c); return ptr; } + const Node *check(void *c) const + { if (!ptr || c!=cont) throw_invalid(c); return ptr; } +#else + Node *check(void *c) + { return ptr; } + const Node *check(void *c) const + { return ptr; } +#endif +protected: + Node *ptr; + void *cont; + friend class GListBase; + friend class GSetBase; + void throw_invalid(void *c) const no_return; +}; + + +class GListBase : public GCont +{ +protected: + GListBase(const Traits& traits); + GListBase(const GListBase &ref); + void append(Node *n); + void prepend(Node *n); + void insert_after(GPosition pos, Node *n); + void insert_before(GPosition pos, Node *n); + void insert_before(GPosition pos, GListBase &fromlist, GPosition &frompos); + void del(GPosition &pos); +protected: + const Traits &traits; + int nelem; + Node head; +public: + ~GListBase(); + GListBase & operator= (const GListBase & gl); + GPosition firstpos() const { return GPosition(head.next, (void*)this); } + GPosition lastpos() const { return GPosition(head.prev, (void*)this); } + int isempty() const { return nelem==0; }; + GPosition nth(unsigned int n) const; + void empty(); +}; + + +template<class TI> +class GListImpl : public GListBase +{ +protected: + GListImpl(); + typedef GCONT ListNode<TI> LNode; + static Node * newnode(const TI &elt); + int operator==(const GListImpl<TI> &l2) const; + int search(const TI &elt, GPosition &pos) const; +}; + +template<class TI> +GListImpl<TI>::GListImpl() + : GListBase( GCONT NormTraits<LNode>::traits() ) +{ +} + +template<class TI> GCONT Node * +GListImpl<TI>::newnode(const TI &elt) +{ + LNode *n = (LNode *) operator new (sizeof(LNode )); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(LNode )); +#endif + new ((void*)&(n->val)) TI(elt); + return (Node*) n; +} + +template<class TI> int +GListImpl<TI>::operator==(const GListImpl<TI> &l2) const +{ + Node *p, *q; + for (p=head.next, q=l2.head.next; p && q; p=p->next, q=q->next ) + if (((LNode*)p)->val != ((LNode*)q)->val) + return 0; + return p==0 && q==0; +} + +template<class TI> int +GListImpl<TI>::search(const TI &elt, GPosition &pos) const +{ + Node *n = (pos ? pos.check((void*)this) : head.next); + for (; n; n=n->next) + if ( ((LNode *)n)->val == elt ) + break; + if (n) pos = GPosition(n, (void*)this); + return (n != 0); +} + + +/** Common base class for all doubly linked lists. + Class \Ref{GListTemplate} implements all methods for manipulating lists + of of objects of type #TYPE#. You should not however create instances of + this class. You should instead use class \Ref{GList} or \Ref{GPList}. */ + +template <class TYPE, class TI> +class GListTemplate : protected GListImpl<TI> +{ +public: + // -- ACCESS + /** Returns the number of elements in the list. */ + int size() const + { return this->nelem; } + /** Returns the first position in the list. See \Ref{GPosition}. */ + GPosition firstpos() const + { return GListImpl<TI>::firstpos(); } + /** Returns the last position in the list. See \Ref{GPosition}. */ + GPosition lastpos() const + { return GListImpl<TI>::lastpos(); } + /** Implicit notation for GList::firstpos(). */ + operator GPosition() const + { return firstpos(); } + /** Returns a reference to the list element at position #pos#. This + reference can be used for both reading (as "#a[n]#") and modifying (as + "#a[n]=v#") a list element. Using an invalid position will cause a + segmentation violation. See \Ref{GPosition} for efficient operations on + positions. */ + TYPE& operator[](GPosition pos) + { return (TYPE&) (((typename GListImpl<TI>::LNode *)pos.check((void*)this))->val); } + /** Returns a constant reference to the list element at position #pos#. + This reference only be used for reading a list element. An exception + \Ref{GException} is thrown if #pos# is not a valid position. This + variant of #operator[]# is necessary when dealing with a #const + GList<TYPE>#. See \Ref{GPosition} for efficient operations on + positions. */ + const TYPE& operator[](GPosition pos) const + { return (const TYPE&) (((const typename GListImpl<TI>::LNode *)pos.check((void*)this))->val); } + // -- TEST + /** Tests whether a list is empty. + Returns a non zero value if the list contains no elements. */ + int isempty() const + { return this->nelem==0; } + /** Compares two lists. Returns a non zero value if and only if both lists + contain the same elements (as tested by #TYPE::operator==(const TYPE&)# + in the same order. */ + int operator==(const GListTemplate<TYPE,TI> &l2) const + { return GListImpl<TI>::operator==(l2); } + // -- SEARCHING + /** Returns the position #pos# of the #n#-th list element. An invalid + position is returned if the list contains less than #n# elements. The + operation works by sequentially scanning the list until reaching the + #n#-th element. */ + GPosition nth(unsigned int n) const + { return GListImpl<TI>::nth(n); } + /* Compatibility */ + int nth(unsigned int n, GPosition &pos) const + { GPosition npos=nth(n); if (npos) pos=npos; return !!pos; } + /** Tests whether the list contains a given element. If the list contains + #elt#, the position of the the first list element equal to #elt# as + checked by #TYPE::operator==(const TYPE&)# is returned. Otherwise an + invalid position is returned. */ + GPosition contains(const TYPE &elt) const + { GPosition pos; GListImpl<TI>::search((const TI&)elt, pos); return pos; } + /** Searches the list for a given element. If position #pos# is a valid + position for this list, the search starts at the specified position. If + position #pos# is not a valid position, the search starts at the + beginning of the list. The list elements are sequentially compared with + #elt# using #TYPE::operator==(const TYPE&)#. As soon as a list element + is equal to #elt#, function #search# sets argument #pos# with the + position of this list element and returns 1. If however the search + reaches the end of the list, function #search# returns 0 and leaves + #pos# unchanged. */ + int search(const TYPE &elt, GPosition &pos) const + { return GListImpl<TI>::search((const TI&)elt, pos); } + // -- ALTERATION + /** Erases the list contents. All list elements are destroyed and + unlinked. The list is left with zero elements. */ + void empty() + { GListImpl<TI>::empty(); } + /** Inserts an element after the last element of the list. + The new element is initialized with a copy of argument #elt#. */ + void append(const TYPE &elt) + { GListImpl<TI>::append(newnode((const TI&)elt)); } + /** Inserts an element before the first element of the list. + The new element is initialized with a copy of argument #elt#. */ + void prepend(const TYPE &elt) + { GListImpl<TI>::prepend(newnode((const TI&)elt)); } + /** Inserts a new element after the list element at position #pos#. When + position #pos# is null the element is inserted at the beginning of the + list. The new element is initialized with a copy of #elt#. */ + void insert_after(GPosition pos, const TYPE &elt) + { GListImpl<TI>::insert_after(pos, newnode((const TI&)elt)); } + /** Inserts a new element before the list element at position #pos#. When + position #pos# is null the element is inserted at the end of the + list. The new element is initialized with a copy of #elt#. */ + void insert_before(GPosition pos, const TYPE &elt) + { GListImpl<TI>::insert_before(pos, newnode((const TI&)elt)); } + /** Inserts an element of another list into this list. This function + removes the element at position #frompos# in list #frompos#, inserts it + in the current list before the element at position #pos#, and advances + #frompos# to the next element in list #fromlist#. When position #pos# is + null the element is inserted at the end of the list. */ + void insert_before(GPosition pos, GListTemplate<TYPE,TI> &fromlist, GPosition &frompos) + { GListImpl<TI>::insert_before(pos, fromlist, frompos); } + /** Destroys the list element at position #pos#. This function does + nothing unless position #pos# is a valid position. */ + void del(GPosition &pos) + { GListImpl<TI>::del(pos); } + /* Old iterators. Do not use. */ +#if GCONTAINER_OLD_ITERATORS + void first(GPosition &pos) const { pos = firstpos(); } + void last(GPosition &pos) const { pos = lastpos(); } + const TYPE *next(GPosition &pos) const + { if (!pos) return 0; const TYPE *x=&((*this)[pos]); ++pos; return x; } + const TYPE *prev(GPosition &pos) const + { if (!pos) return 0; const TYPE *x=&((*this)[pos]); --pos; return x; } + TYPE *next(GPosition &pos) + { if (!pos) return 0; TYPE *x=&((*this)[pos]); ++pos; return x; } + TYPE *prev(GPosition &pos) + { if (!pos) return 0; TYPE *x=&((*this)[pos]); --pos; return x; } +#endif +}; + + +/** Doubly linked lists. Template class #GList<TYPE># implements a doubly + linked list of elements of type #TYPE#. This class only implement + constructors. See class \Ref{GListTemplate} and \Ref{GPosition} for a + description of all access methods. */ + +template <class TYPE> +class GList : public GListTemplate<TYPE,TYPE> +{ +public: + /** Null Constructor. Constructs a list with zero elements. */ + GList() : GListTemplate<TYPE,TYPE>() {} + GList& operator=(const GList &r) + { GListBase::operator=(r); return *this; } +}; + + +/** Doubly linked lists for smart pointers. + Template class #GList<TYPE># implements a doubly linked list of elements + of type #GP<TYPE># (see \Ref{GSmartPointer.h}). Significantly smaller + code sizes can be achieved by using this class instead of the more general + #GArray<GP<TYPE>>#. + This class only implement constructors. See class \Ref{GListTemplate} and + \Ref{GPosition} for a description of all access methods. */ + +template <class TYPE> +class GPList : public GListTemplate<GP<TYPE>,GPBase> +{ +public: + /** Null Constructor. Constructs a list with zero elements. */ + GPList() : GListTemplate<GP<TYPE>,GPBase>() {} + GPList& operator=(const GPList &r) + { GListBase::operator=(r); return *this; } +}; + + +//@} + + + +// ------------------------------------------------------------ +// ASSOCIATIVE MAPS +// ------------------------------------------------------------ + +/** @name Associative Maps + + These template classes implements a associative maps. The associative map + contains an arbitrary number of entries. Each entry is a pair containing + one element of type #KTYPE# (named the "key") and one element of type + #VTYPE# (named the "value"). All entries have distinct keys. + These template class must be able to access the following functions: + \begin{itemize} + \item a #VTYPE# default constructor #VTYPE::VTYPE()#, + \item a #VTYPE# copy constructor #VTYPE::VTYPE(const VTYPE &)#, + \item optionally a #VTYPE# destructor #VTYPE::~VTYPE()#, + \item a #KTYPE# default constructor #KTYPE::KTYPE()#, + \item a #KTYPE# copy constructor #KTYPE::KTYPE(const KTYPE &)#, + \item optionally a #KTYPE# destructor #KTYPE::~KTYPE()#, + \item a #KTYPE# comparison operator #KTYPE::operator==(const KTYPE &)#, + \item and a #KTYPE# hashing function #hash(const KTYPE&)#. + \end{itemize} + The hashing function must return an #unsigned int# number. Multiple + invocations of the hashing function with equal arguments (in the sense of + #KTYPE::operator==#) must always return the same number. + Position objects (see \Ref{GPosition}) may be used to iterate over the + entries contained by an associative map. + @memo Associative maps. +*/ +//@{ + +class GSetBase : public GCont +{ +protected: + GSetBase(const Traits &traits); + GSetBase(const GSetBase &ref); + static GCONT HNode *newnode(const void *key); + HNode *hashnode(unsigned int hashcode) const; + HNode *installnode(HNode *n); + void deletenode(HNode *n); +protected: + const Traits &traits; + int nelems; + int nbuckets; + HNode **table; + GPBuffer<HNode *> gtable; + HNode *first; +private: + void insertnode(HNode *n); + void rehash(int newbuckets); +public: + ~GSetBase(); + GSetBase& operator=(const GSetBase &ref); + GPosition firstpos() const; + void del(GPosition &pos); + void empty(); +}; + +template <class K> +class GSetImpl : public GSetBase +{ +protected: + GSetImpl(); + GSetImpl(const Traits &traits); + typedef GCONT SetNode<K> SNode; + HNode *get(const K &key) const; + HNode *get_or_throw(const K &key) const; + HNode *get_or_create(const K &key); +public: + GPosition contains(const K &key) const + { return GPosition( get(key), (void*)this); } + void del(const K &key) + { deletenode(get(key)); } +}; + +template<class K> +GSetImpl<K>::GSetImpl() + : GSetBase( GCONT NormTraits<GCONT SetNode<K> >::traits() ) +{ +} + +template<class K> +GSetImpl<K>::GSetImpl(const Traits &traits) + : GSetBase(traits) +{ +} + +template<class K> GCONT HNode * +GSetImpl<K>::get(const K &key) const +{ + unsigned int hashcode = hash(key); + for (SNode *s=(SNode*)hashnode(hashcode); s; s=(SNode*)(s->hprev)) + if (s->hashcode == hashcode && s->key == key) return s; + return 0; +} + +#if GCONTAINER_BOUNDS_CHECK +template<class K> GCONT HNode * +GSetImpl<K>::get_or_throw(const K &key) const +{ + HNode *m = get(key); + if (!m) + { + G_THROW( ERR_MSG("GContainer.cannot_add") ); + } + return m; +} +#else +template<class K> inline GCONT HNode * +GSetImpl<K>::get_or_throw(const K &key) const +{ + return get(key); +} +#endif + +template<class K> GCONT HNode * +GSetImpl<K>::get_or_create(const K &key) +{ + HNode *m = get(key); + if (m) return m; + SNode *n = (SNode*) operator new (sizeof(SNode)); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(SNode)); +#endif + new ((void*)&(n->key)) K ( key ); + n->hashcode = hash((const K&)(n->key)); + installnode(n); + return n; +} + +template <class K, class TI> +class GMapImpl : public GSetImpl<K> +{ +protected: + GMapImpl(); + GMapImpl(const GCONT Traits &traits); + typedef GCONT MapNode<K,TI> MNode; + GCONT HNode* get_or_create(const K &key); +}; + +template<class K, class TI> +GMapImpl<K,TI>::GMapImpl() + : GSetImpl<K> ( GCONT NormTraits<GCONT MapNode<K,TI> >::traits() ) +{ +} + +template<class K, class TI> +GMapImpl<K,TI>::GMapImpl(const GCONT Traits &traits) + : GSetImpl<K>(traits) +{ +} + +template<class K, class TI> GCONT HNode * +GMapImpl<K,TI>::get_or_create(const K &key) +{ + GCONT HNode *m = get(key); + if (m) return m; + MNode *n = (MNode*) operator new (sizeof(MNode)); +#if GCONTAINER_ZERO_FILL + memset(n, 0, sizeof(MNode)); +#endif + new ((void*)&(n->key)) K (key); + new ((void*)&(n->val)) TI (); + n->hashcode = hash((const K&)(n->key)); + installnode(n); + return n; +} + + + +/** Common base class for all associative maps. + Class \Ref{GArrayTemplate} implements all methods for manipulating + associative maps with key type #KTYPE# and value type #VTYPE#. + You should not however create instances of this class. + You should instead use class \Ref{GMap} or \Ref{GPMap}. */ + +template <class KTYPE, class VTYPE, class TI> +class GMapTemplate : protected GMapImpl<KTYPE,TI> +{ +public: + /** Returns the number of elements in the map. */ + int size() const + { return this->nelems; } + /** Returns the first position in the map. */ + GPosition firstpos() const + { return GMapImpl<KTYPE,TI>::firstpos(); } + /** Implicit notation for GMap::firstpos(). */ + operator GPosition() const + { return firstpos(); } + /** Tests whether the associative map is empty. + Returns a non zero value if and only if the map contains zero entries. */ + int isempty() const + { return this->nelems==0; } + /** Searches an entry for key #key#. If the map contains an entry whose key + is equal to #key# according to #KTYPE::operator==(const KTYPE&)#, this + function returns its position. Otherwise it returns an invalid + position. */ + GPosition contains(const KTYPE &key) const + { return GMapImpl<KTYPE,TI>::contains(key); } + /* Compatibility */ + GPosition contains(const KTYPE &key, GPosition &pos) const + { return pos = GMapImpl<KTYPE,TI>::contains(key); } + // -- ALTERATION + /** Erases the associative map contents. All entries are destroyed and + removed. The map is left with zero entries. */ + void empty() + { GMapImpl<KTYPE,TI>::empty(); } + /** Returns a constant reference to the key of the map entry at position + #pos#. An exception \Ref{GException} is thrown if position #pos# is not + valid. There is no direct way to change the key of a map entry. */ + const KTYPE &key(const GPosition &pos) const + { return (const KTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->key); } + /** Returns a reference to the value of the map entry at position #pos#. + This reference can be used for both reading (as "#a[n]#") and modifying + (as "#a[n]=v#"). An exception \Ref{GException} is thrown if position + #pos# is not valid. */ + VTYPE& operator[](const GPosition &pos) + { return (VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->val); } + /** Returns a constant reference to the value of the map entry at position + #pos#. This reference can only be used for reading (as "#a[n]#") the + entry value. An exception \Ref{GException} is thrown if position #pos# + is not valid. */ + const VTYPE& operator[](const GPosition &pos) const + { return (const VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(pos.check((void*)this)))->val); } + /** Returns a constant reference to the value of the map entry for key + #key#. This reference can only be used for reading (as "#a[n]#") the + entry value. An exception \Ref{GException} is thrown if no entry + contains key #key#. This variant of #operator[]# is necessary when + dealing with a #const GMAP<KTYPE,VTYPE>#. */ + const VTYPE& operator[](const KTYPE &key) const + { return (const VTYPE&)(((const typename GMapImpl<KTYPE,TI>::MNode*)(get_or_throw(key)))->val); } + /** Returns a reference to the value of the map entry for key #key#. This + reference can be used for both reading (as "#a[n]#") and modifying (as + "#a[n]=v#"). If there is no entry for key #key#, a new entry is created + for that key with the null constructor #VTYPE::VTYPE()#. */ + VTYPE& operator[](const KTYPE &key) + { return (VTYPE&)(((typename GMapImpl<KTYPE,TI>::MNode*)(get_or_create(key)))->val); } + /** Destroys the map entry for position #pos#. + Nothing is done if position #pos# is not a valid position. */ + void del(GPosition &pos) + { GSetBase::del(pos); } + /** Destroys the map entry for key #key#. + Nothing is done if there is no entry for key #key#. */ + void del(const KTYPE &key) + { GMapImpl<KTYPE,TI>::del(key); } + /* Old iterators. Do not use. */ +#if GCONTAINER_OLD_ITERATORS + void first(GPosition &pos) const { pos = firstpos(); } + const VTYPE *next(GPosition &pos) const + { if (!pos) return 0; const VTYPE *x=&((*this)[pos]); ++pos; return x; } + VTYPE *next(GPosition &pos) + { if (!pos) return 0; VTYPE *x=&((*this)[pos]); ++pos; return x; } +#endif +}; + + + +/** Associative maps. + Template class #GMap<KTYPE,VTYPE># implements an associative map. + The map contains an arbitrary number of entries. Each entry is a + pair containing one element of type #KTYPE# (named the "key") and one + element of type #VTYPE# (named the "value"). + The entry associated to a particular value of the key can retrieved + very efficiently. + This class only implement constructors. See class \Ref{GMapTemplate} and + \Ref{GPosition} for a description of all access methods.*/ + +template <class KTYPE, class VTYPE> +class GMap : public GMapTemplate<KTYPE,VTYPE,VTYPE> +{ +public: + // -- ACCESS + GMap() : GMapTemplate<KTYPE,VTYPE,VTYPE>() {} + GMap& operator=(const GMap &r) + { GSetBase::operator=(r); return *this; } +}; + +/** Associative maps for smart-pointers. + Template class #GMap<KTYPE,VTYPE># implements an associative map for key + type #KTYPE# and value type #GP<VTYPE># (see \Ref{GSmartPointer.h}). The + map contains an arbitrary number of entries. Each entry is a pair + containing one element of type #KTYPE# (named the "key") and one aelement + of type #VTYPE# (named the "value"). The entry associated to a particular + value of the key can retrieved very efficiently. + Significantly smaller code sizes can be achieved by using this class + instead of the more general #GMap<KTYPE,GP<VTYPE>># (see \Ref{GMap}). + This class only implement constructors. See class \Ref{GMapTemplate} and + \Ref{GPosition} for a description of all access methods.*/ + +template <class KTYPE, class VTYPE> +class GPMap : public GMapTemplate<KTYPE,GP<VTYPE>,GPBase> +{ +public: + GPMap() : GMapTemplate<KTYPE,GP<VTYPE>,GPBase>() {} + GPMap& operator=(const GPMap &r) + { GSetBase::operator=(r); return *this; } +}; + + +// ------------------------------------------------------------ +// HASH FUNCTIONS +// ------------------------------------------------------------ + + +/** @name Hash functions + These functions let you use template class \Ref{GMap} with the + corresponding elementary types. The returned hash code may be reduced to + an arbitrary range by computing its remainder modulo the upper bound of + the range. + @memo Hash functions for elementary types. */ +//@{ + +/** Hashing function (unsigned int). */ +static inline unsigned int +hash(const unsigned int & x) +{ + return x; +} + +/** Hashing function (int). */ +static inline unsigned int +hash(const int & x) +{ + return (unsigned int)x; +} + +/** Hashing function (long). */ +static inline unsigned int +hash(const long & x) +{ + return (unsigned int)x; +} + +/** Hashing function (unsigned long). */ +static inline unsigned int +hash(const unsigned long & x) +{ + return (unsigned int)x; +} + +/** Hashing function (void *). */ +static inline unsigned int +hash(void * const & x) +{ + return (unsigned long) x; +} + +/** Hashing function (const void *). */ +static inline unsigned int +hash(const void * const & x) +{ + return (unsigned long) x; +} + +/** Hashing function (float). */ +static inline unsigned int +hash(const float & x) +{ + // optimizer will get rid of unnecessary code + unsigned int *addr = (unsigned int*)&x; + if (sizeof(float)<2*sizeof(unsigned int)) + return addr[0]; + else + return addr[0]^addr[1]; +} + +/** Hashing function (double). */ +static inline unsigned int +hash(const double & x) +{ + // optimizer will get rid of unnecessary code + unsigned int *addr = (unsigned int*)&x; + if (sizeof(double)<2*sizeof(unsigned int)) + return addr[0]; + else if (sizeof(double)<4*sizeof(unsigned int)) + return addr[0]^addr[1]; + else + return addr[0]^addr[1]^addr[2]^addr[3]; +} + + +//@} +//@} +//@} + +// ------------ THE END + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/GException.cpp b/kviewshell/plugins/djvu/libdjvu/GException.cpp new file mode 100644 index 00000000..f3f84dda --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GException.cpp @@ -0,0 +1,284 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GException.cpp,v 1.14 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "GException.h" +#include "DjVuMessageLite.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// - Author: Leon Bottou, 05/1997 + +GException::GException() + : cause(0), file(0), func(0), line(0), source(GException::GINTERNAL) +{ +} + +const char * const +GException::outofmemory = ERR_MSG("GException.outofmemory"); + +GException::GException(const GException & exc) + : file(exc.file), func(exc.func), line(exc.line), source(exc.source) +{ + if (exc.cause && exc.cause!=outofmemory) + { + char *s = new char[strlen(exc.cause)+1]; + strcpy(s, exc.cause); + cause = s; + } + else + { + cause = exc.cause; + } +} + +GException::GException (const char *xcause, const char *file, int line, + const char *func, const source_type xsource) + : file(file), func(func), line(line), source(xsource) +{ + // good place to set a breakpoint and DEBUG message too. + // It'd hard to track exceptions which seem to go from nowhere +#ifdef DEBUG_MSG + DEBUG_MSG("GException::GException(): cause=" << (xcause ? xcause : "unknown") << "\n"); +#endif + if (xcause && xcause!=outofmemory) + { + char *s = new char[strlen(xcause)+1]; + strcpy(s, xcause); + cause = s; + } + else + { + cause = xcause; + } +} + +GException::~GException(void) +{ + if (cause && cause!=outofmemory ) + delete [] const_cast<char*>(cause); + cause=file=func=0; +} + +GException & +GException::operator=(const GException & exc) +{ + if (cause && cause!=outofmemory) + delete [] const_cast<char*>(cause); + cause = 0; + file = exc.file; + func = exc.func; + line = exc.line; + source=exc.source; + if (exc.cause && exc.cause!=outofmemory) + { + char *s = new char[strlen(exc.cause)+1]; + strcpy(s, exc.cause); + cause = s; + } + else + { + cause = exc.cause; + } + return *this; +} + +void +GException::perror(void) const +{ + fflush(0); + DjVuPrintErrorUTF8("*** "); + DjVuMessageLite::perror(get_cause()); + if (file && line>0) + DjVuPrintErrorUTF8("*** (%s:%d)\n", file, line); + else if (file) + DjVuPrintErrorUTF8("*** (%s)\n", file); + if (func) + DjVuPrintErrorUTF8("*** '%s'\n", func); + DjVuPrintErrorUTF8("\n"); +} + +const char* +GException::get_cause(void) const +{ + if (! cause) + return "Invalid exception"; + return cause; +} + +int +GException::cmp_cause(const char s1[] , const char s2[]) +{ + int retval; + if(! s2 || !s2[0]) + { + retval=(s1&&s1[0])?1:(-1); + }else if(! s1 || !s1[0]) + { + retval=(-1); + }else + { + const char *end_s1=strpbrk(s1,"\t\n"); + const int n1=end_s1?(int)((size_t)end_s1-(size_t)s1):strlen(s1); + const char *end_s2=strpbrk(s1,"\t\n"); + const int n2=end_s2?(int)((size_t)end_s2-(size_t)s2):strlen(s2); + retval=(n1==n2)?strncmp(s1,s2,n1):strcmp(s1,s2); + } + return retval; +} + +int +GException::cmp_cause(const char s2[]) const +{ + return cmp_cause(cause,s2); +} + +#ifdef USE_EXCEPTION_EMULATION + +GExceptionHandler *GExceptionHandler::head = 0; + +void +GExceptionHandler::emthrow(const GException &gex) +{ + if (head) + { + head->current = gex; + longjmp(head->jump, -1); + } + else + { + DjVuPrintErrorUTF8("\n*** Unhandled exception"); + gex.perror(); + abort(); + } +} + +#else // ! USE_EXCEPTION_EMULATION + +static int abort_on_exception = 0; + +void +#ifndef NO_LIBGCC_HOOKS +GExceptionHandler::exthrow(const GException &ex) +#else +GExceptionHandler::exthrow(const GException ex) +#endif /* NO_LIBGCC_HOOKS */ +{ + if (abort_on_exception) + abort(); + throw ex; +} + +void +GExceptionHandler::rethrow(void) +{ + if (abort_on_exception) + abort(); + throw; +} + +#endif + + + +// ------ MEMORY MANAGEMENT HANDLER + +#ifndef NEED_DJVU_MEMORY +// This is not activated when C++ memory management +// is overidden. The overriding functions handle +// memory exceptions by themselves. +# if defined(_MSC_VER) +// Microsoft is different! +static int throw_memory_error(size_t) { G_THROW(GException::outofmemory); return 0; } +static int (*old_handler)(size_t) = _set_new_handler(throw_memory_error); +# else // !_MSC_VER +// Standard C++ +static void throw_memory_error() { G_THROW(GException::outofmemory); } +# if !defined(WIN32) && !defined(__CYGWIN32__) && !defined(OS2) +# ifdef HAVE_STDINCLUDES +static void (*old_handler)() = std::set_new_handler(throw_memory_error); +# else +static void (*old_handler)() = set_new_handler(throw_memory_error); +# endif // HAVE_STDINCLUDES +# endif // ! WIN32 +# endif // !_MSC_VER +#endif // !NEED_DJVU_MEMORY + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GException.h b/kviewshell/plugins/djvu/libdjvu/GException.h new file mode 100644 index 00000000..97286987 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GException.h @@ -0,0 +1,356 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GException.h,v 1.10 2005/06/07 23:42:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GEXCEPTION_H_ +#define _GEXCEPTION_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#ifndef no_return +#ifdef __GNUC__ +#define no_return __attribute__ ((noreturn)) +#else +#define no_return +#endif +#endif + +/** @name GException.h + + Files #"GException.h"# and #"GException.cpp"# define a portable exception + scheme used through the DjVu Reference Library. This scheme can use native + C++ exceptions or an exception emulation based on #longjmp#/#setjmp#. A + particular model can be forced a compile time by defining option + #CPP_SUPPORTS_EXCEPTIONS# or #USE_EXCEPTION_EMULATION#. + + This emulation code was motivated because many compilers did not properly + support exceptions as mandated by the C++ standard documents. This + emulation is now considered obsolete because (a) it is not able to call + the proper destructors when an exception occurs, and (b) it is not thread + safe. Although all modern C++ compiler handle exception decently, the + exception handling intrinsics are not always thread safe. Therefore we + urge programmers to {\em only} use exceptions to signal error conditions + that force the library to discontinue execution. + + There are four macros for handling exceptions. Macros #G_TRY#, #G_CATCH# and + #G_ENDCATCH# are used to define an exception catching block. Exceptions can + be thrown at all times using macro #G_THROW(cause)#. An exception can be + re-thrown from a catch block using macro #G_RETHROW#. + + Example: + \begin{verbatim} + G_TRY + { + // program lines which may result in a call to THROW() + G_THROW("message"); + } + G_CATCH(ex) + { + // Variable ex refers to a GException object. + ex.perror(); + // You can rethrow the exception to an outer exception handler. + G_RETHROW; + } + G_ENDCATCH; + \end{verbatim} + + @memo + Portable exceptions. + @author + L\'eon Bottou <[email protected]> -- initial implementation.\\ + Andrei Erofeev <[email protected]> -- fixed message memory allocation. + @version + #$Id: GException.h,v 1.10 2005/06/07 23:42:22 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** Exception class. + The library always uses macros #G_TRY#, #G_THROW#, #G_CATCH# and #G_ENDCATCH# for + throwing and catching exceptions (see \Ref{GException.h}). These macros + only deal with exceptions of type #GException#. */ + +class GException { +public: + enum source_type { GINTERNAL=0, GEXTERNAL, GAPPLICATION, GOTHER }; + /** Constructs a GException. This constructor is usually called by macro + #G_THROW#. Argument #cause# is a plain text error message. As a + convention, string #ByteStream::EndOfFile# is used when reaching an unexpected + end-of-file condition and string #DataPool::Stop# is used when the user + interrupts the execution. The remaining arguments are usually provided + by the predefined macros #__FILE__#, #__LINE__#, and (G++ and EGCS only) + #__PRETTY_FUNCTION__#. */ + GException (const char *cause, const char *file=0, int line=0, + const char *func=0, const source_type source=GINTERNAL); + + /** Copy Constructor. */ + GException (const GException & exc); + + /** Null Constructor. */ + GException (); + + /** Destructor. */ + virtual ~GException(void); + + /** Copy Operator. */ + GException & operator=(const GException & exc); + + /** Prints an error message on stderr. + This function no longer takes a message parameter because + some instances used a i18n message id and other instances + used a literal string. */ + void perror(void) const; + + /** Returns the string describing the cause of the exception. The returned + pointer is never null. Exception handlers should not rely on the value + of the string #cause#. As a convention however, string + #ByteStream::EndOfFile# is used + when reaching an unexpected end-of-file condition and string + #DataPool::Stop# is used when the user interrupts the execution. These + strings can be tested by the exception handlers, with + #cmp_cause#. Similar conventional strings may be defined + in the future. They all will be small strings with only uppercase + characters. */ + const char* get_cause(void) const; + + /** Compares the cause with the specified string, ignoring anything after + the first tab. */ + int cmp_cause(const char s2[]) const; + + /** Compares the cause with the specified string, ignoring anything after + the first tab. */ + static int cmp_cause(const char s1[],const char s2[]); + + /** Returns the function name from which the exception was thrown. + A null pointer is returned if no function name is available. */ + const char* get_function(void) const { return func; } + + /** Returns the file name from which the exception was thrown. + A null pointer is returned if no file name is available. */ + const char* get_file(void) const { return file; } + + /** Returns the exception source */ + source_type get_source(void) const { return source; } + + /** Returns the line number from which the exception was thrown. + A zero is returned if no line number is available. */ + int get_line(void) const { return line; }; + + // Magic cause string + static const char * const outofmemory; + +private: + const char *cause; + const char *file; + const char *func; + int line; + source_type source; +}; + +//@} + +#undef G_TRY +#undef G_CATCH +#undef G_CATCH_ALL +#undef G_ENDCATCH +#undef G_RETHROW +#undef G_THROW +#undef G_THROW_TYPE +#undef G_THROW_INTERNAL +#undef G_THROW_EXTERNAL +#undef G_THROW_APPLICATION +#undef G_THROW_OTHER + +// Check if compiler supports native exceptions +#if defined(_MSC_VER) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +#if defined(__MWERKS__) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +#if defined(__EXCEPTIONS) +#define CPP_SUPPORTS_EXCEPTIONS +#endif +// Decide which exception model to use +#ifndef CPP_SUPPORTS_EXCEPTIONS +#ifndef USE_EXCEPTION_EMULATION +#define USE_EXCEPTION_EMULATION +#endif +#endif + + +#ifndef USE_EXCEPTION_EMULATION + +// Compiler supports ANSI C++ exceptions. +// Defined exception macros accordingly. + +class GExceptionHandler { +public: +#ifndef NO_LIBGCC_HOOKS + static void exthrow(const GException &) no_return; +#else + static void exthrow(const GException ) no_return; +#endif /* NO_LIBGCC_HOOKS */ + static void rethrow(void) no_return; +}; + +#define G_TRY try +#define G_CATCH(n) catch(const GException &n) { +#define G_CATCH_ALL catch(...) { +#define G_ENDCATCH } +#define G_RETHROW GExceptionHandler::rethrow() +#define G_EMTHROW(ex) GExceptionHandler::exthrow(ex) +#ifdef __GNUG__ +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::exthrow \ + (GException(msg, __FILE__, __LINE__, __PRETTY_FUNCTION__, xtype)) +#else +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::exthrow \ + (GException(msg, __FILE__, __LINE__,0, xtype)) +#endif + +#else // USE_EXCEPTION_EMULATION + +// Compiler does not support ANSI C++ exceptions. +// Emulate with setjmp/longjmp. + +#include <setjmp.h> + +class GExceptionHandler { +public: + jmp_buf jump; + GExceptionHandler *next; + GException current; +public: + static GExceptionHandler *head; + static void emthrow(const GException &) no_return; +public: + GExceptionHandler() { next = head; }; + ~GExceptionHandler() { head = next; }; +}; + +#define G_TRY do { GExceptionHandler __exh; \ + if (!setjmp(__exh.jump)) \ + { GExceptionHandler::head = &__exh; + +#define G_CATCH_ALL } else { GExceptionHandler::head = __exh.next; +#define G_CATCH(n) G_CATCH_ALL const GException& n = __exh.current; + +#define G_ENDCATCH } } while(0) + +#define G_RETHROW GExceptionHandler::emthrow(__exh.current) + +#ifdef __GNUG__ +#define G_THROW_TYPE(msg,xtype) GExceptionHandler::emthrow \ + (GException(msg, __FILE__, __LINE__, __PRETTY_FUNCTION__, xtype)) +#define G_EMTHROW(ex) GExceptionHandler::emthrow(ex) +#else +#define G_THROW_TYPE(m,xtype) GExceptionHandler::emthrow \ + (GException(m, __FILE__, __LINE__,0, xtype)) +#define G_EMTHROW(ex) GExceptionHandler::emthrow(ex) +#endif + +#endif // !CPP_SUPPORTS_EXCEPTIONS + + +inline void +G_EXTHROW +(const GException &ex,const char *msg=0,const char *file=0,int line=0, + const char *func=0, const GException::source_type source=GException::GINTERNAL) +{ + G_EMTHROW( (msg||file||line||func)? + GException(msg?msg:ex.get_cause(), + file?file:ex.get_file(), + line?line:ex.get_line(), + func?func:ex.get_function(), + source) + :ex); +} + +inline void +G_EXTHROW +(const char msg[],const char *file=0,int line=0,const char *func=0, + const GException::source_type source=GException::GINTERNAL ) +{ + G_EMTHROW(GException(msg,file,line,func,source)); +} + +#define G_THROW(msg) G_THROW_TYPE(msg,GException::GINTERNAL) +#define G_THROW_INTERNAL(msg) G_THROW_TYPE(msg,GException::GINTERNAL) +#define G_THROW_EXTERNAL(msg) G_THROW_TYPE(msg,GException::GEXTERNAL) +#define G_THROW_APPLICATION(msg) G_THROW_TYPE(msg,GException::GAPPLICATION) +#define G_THROW_OTHER(msg) G_THROW_TYPE(msg,GException::GOTHER) + +// -------------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp b/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp new file mode 100644 index 00000000..973c6cec --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp @@ -0,0 +1,663 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GIFFManager.cpp,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GIFFManager.h" +#include "GException.h" +#include "debug.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GIFFChunk::~GIFFChunk(void) {} + +GIFFManager::~GIFFManager(void) {} + +GP<GIFFManager> +GIFFManager::create(void) +{ + GIFFManager *iff=new GIFFManager(); + GP<GIFFManager> retval=iff; + iff->init(); + return retval; +} + +GP<GIFFManager> +GIFFManager::create(const GUTF8String &name) +{ + GIFFManager *iff=new GIFFManager(); + GP<GIFFManager> retval=iff; + iff->init(name); + return retval; +} + +void +GIFFChunk::set_name(GUTF8String name) +{ + DEBUG_MSG("GIFFChunk::set_name(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + const int colon=name.search(':'); + if(colon>=0) + { + type=name.substr(0,colon); + name=name.substr(colon+1,(unsigned int)-1); + if(name.search(':')>=0) + G_THROW( ERR_MSG("GIFFManager.one_colon") ); + } + + DEBUG_MSG("auto-setting type to '" << type << "'\n"); + + if (name.contains(".[]")>=0) + G_THROW( ERR_MSG("GIFFManager.bad_char") ); + + strncpy(GIFFChunk::name, (const char *)name, 4); + GIFFChunk::name[4]=0; + for(int i=strlen(GIFFChunk::name);i<4;i++) + GIFFChunk::name[i]=' '; +} + +bool +GIFFChunk::check_name(GUTF8String name) +{ + GUTF8String type; + const int colon=name.search(':'); + if(colon>=0) + { + type=name.substr(0,colon); + name=name.substr(colon+1,(unsigned int)-1); + } + + const GUTF8String sname=(name.substr(0,4)+" ").substr(0,4); + + DEBUG_MSG("GIFFChunk::check_name(): type='" << type << "' name='" << sname << "'\n"); + return (type==GIFFChunk::type || !type.length() && GIFFChunk::type=="FORM") + && sname==GIFFChunk::name; +} + +void +GIFFChunk::save(IFFByteStream & istr, bool use_trick) +{ + DEBUG_MSG("GIFFChunk::save(): saving chunk '" << get_full_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (is_container()) + { + istr.put_chunk(get_full_name(), use_trick); + if (chunks.size()) + { + GPosition pos; + for(pos=chunks;pos;++pos) + if (chunks[pos]->get_type()=="PROP") + chunks[pos]->save(istr); + for(pos=chunks;pos;++pos) + if (chunks[pos]->get_type()!="PROP") + chunks[pos]->save(istr); + } else + { + DEBUG_MSG("but it's empty => saving empty container.\n"); + } + istr.close_chunk(); + } else + { + istr.put_chunk(get_name(), use_trick); + istr.get_bytestream()->writall((const char *) data, data.size()); + istr.close_chunk(); + } +} + +void +GIFFChunk::add_chunk(const GP<GIFFChunk> & chunk, int position) +{ + DEBUG_MSG("GIFFChunk::add_chunk(): Adding chunk to '" << get_name() << + "' @ position=" << position << "\n"); + DEBUG_MAKE_INDENT(3); + + if (!type.length()) + { + DEBUG_MSG("Converting the parent to FORM\n"); + type="FORM"; + } + + if (chunk->get_type()=="PROP") + { + DEBUG_MSG("Converting the parent to LIST\n"); + type="LIST"; + } + + GPosition pos; + if (position>=0 && chunks.nth(position, pos)) + { + chunks.insert_before(pos, chunk); + }else + { + chunks.append(chunk); + } +} + +GUTF8String +GIFFChunk::decode_name(const GUTF8String &name, int &number) +{ + DEBUG_MSG("GIFFChunk::decode_name(): Checking brackets in name '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (name.search('.')>=0) + G_THROW( ERR_MSG("GIFFManager.no_dots") ); + + number=0; + const int obracket=name.search('['); + GUTF8String short_name; + if (obracket >= 0) + { + const int cbracket=name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); + if (name.length() > (unsigned int)(cbracket+1)) + G_THROW( ERR_MSG("GIFFManager.garbage") ); +// number =atoi((const char *)name.substr(obracket+1,cbracket-obracket-1)); + number= name.substr(obracket+1,cbracket-obracket-1).toInt(); + short_name=name.substr(0,obracket); + }else + { + short_name=name; + } + + const int colon=short_name.search(':'); + if (colon>=0) + short_name=short_name.substr(colon+1,(unsigned int)-1); + + for(int i=short_name.length();i<4;i++) + short_name.setat(i, ' '); + + DEBUG_MSG("short_name='" << short_name << "'\n"); + DEBUG_MSG("number=" << number << "\n"); + + return short_name; +} + +void +GIFFChunk::del_chunk(const GUTF8String &name) + // The name may contain brackets to specify the chunk number +{ + DEBUG_MSG("GIFFChunk::del_chunk(): Deleting chunk '" << name << + "' from '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int number; + const GUTF8String short_name=decode_name(name,number); + + GPosition pos=chunks; + for(int num=0;pos;++pos) + { + if ((chunks[pos]->get_name()==short_name)&&(num++ == number)) + { + chunks.del(pos); + break; + } + } + if(! pos) + { + G_THROW( ERR_MSG("GIFFManager.no_chunk") "\t"+short_name+"\t"+GUTF8String(number)+"\t"+get_name()); + } +} + +GP<GIFFChunk> +GIFFChunk::get_chunk(const GUTF8String &name, int * pos_ptr) + // The name may contain brackets to specify the chunk number +{ + DEBUG_MSG("GIFFChunk::get_chunk(): Returning chunk '" << name << + "' from '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int number; + const GUTF8String short_name=decode_name(name,number); + + int num=0; + int pos_num; + GP<GIFFChunk> retval; + GPosition pos; + for(pos=chunks, pos_num=0;pos;++pos, pos_num++) + { + if (chunks[pos]->get_name()==short_name && num++==number) + { + if (pos_ptr) + *pos_ptr=pos_num; + retval=chunks[pos]; + break; + } + } + return retval; +} + +int +GIFFChunk::get_chunks_number(void) +{ + DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name << + "' in '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + return chunks.size(); +} + +int +GIFFChunk::get_chunks_number(const GUTF8String &name) +{ + DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name << + "' in '" << get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (name.contains("[]")>=0) + G_THROW( ERR_MSG("GIFFManager.no_brackets") ); + + int number; + GUTF8String short_name=decode_name(name,number); + + int num=0; + for(GPosition pos=chunks;pos;++pos) + num+=(chunks[pos]->get_name()==short_name); + return num; +} + +//************************************************************************ + +void +GIFFManager::add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk, + int pos) + // parent_name is the fully qualified name of the PARENT + // IT MAY BE EMPTY + // All the required chunks will be created + // pos=-1 means to append the chunk +{ + DEBUG_MSG("GIFFManager::add_chunk(): Adding chunk to name='" << parent_name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!top_level->get_name().length()) + { + if ((!parent_name.length())||(parent_name[0]!='.')) + G_THROW( ERR_MSG("GIFFManager.no_top_name") ); + if (parent_name.length() < 2) + { + // 'chunk' is actually the new top-level chunk + DEBUG_MSG("since parent_name=='.', making the chunk top-level\n"); + if (!chunk->is_container()) + G_THROW( ERR_MSG("GIFFManager.no_top_cont") ); + top_level=chunk; + return; + } + + DEBUG_MSG("Setting the name of the top-level chunk\n"); + const int next_dot=parent_name.search('.',1); + if(next_dot>=0) + { + top_level->set_name(parent_name.substr(1,next_dot-1)); + }else + { + top_level->set_name(parent_name.substr(1,(unsigned int)-1)); + } + } + + DEBUG_MSG("top level chunk name='" << top_level->get_name() << "'\n"); + + if (parent_name.length() && parent_name[0] == '.') + { + int next_dot=parent_name.search('.',1); + if(next_dot<0) + { + next_dot=parent_name.length(); + } + GUTF8String top_name=parent_name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name") "\t"+top_name); + parent_name=parent_name.substr(next_dot,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *)parent_name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start) + { + GUTF8String name(start,end-start); + GUTF8String short_name; + int number=0; + const int obracket=name.search('['); + if (obracket >= 0) + { + const int cbracket=name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); +// number=atoi((const char *)name.substr(obracket+1,cbracket-obracket-1)); + number = name.substr(obracket+1,cbracket-obracket-1).toInt(); + short_name=name.substr(0,obracket); + }else + { + short_name=name; + } + + for(int i=cur_sec->get_chunks_number(short_name);i<number+1;i++) + cur_sec->add_chunk(GIFFChunk::create(short_name)); + cur_sec=cur_sec->get_chunk(name); + if (!cur_sec) + G_THROW( ERR_MSG("GIFFManager.unknown") "\t"+name); + } + } while(*end); + cur_sec->add_chunk(chunk, pos); +} + +void +GIFFManager::add_chunk(GUTF8String name, const TArray<char> & data) + // name is fully qualified name of the chunk TO BE INSERTED. + // it may contain brackets at the end to set the position + // All the required chunks will be created +{ + DEBUG_MSG("GIFFManager::add_chunk(): adding plain chunk with name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + GUTF8String chunk_name; + const int lastdot=name.rsearch('.'); + if(lastdot < 0) + { + chunk_name=name; + name=name.substr(0,lastdot); + }else + { + chunk_name=name.substr(lastdot+1,(unsigned int)-1); + } + + int pos=-1; + const int obracket=chunk_name.search('['); + if (obracket >= 0) + { + const int cbracket=chunk_name.search(']',obracket+1); + if (cbracket < 0) + G_THROW( ERR_MSG("GIFFManager.unmatched") ); + if (name.length() > (unsigned int)(cbracket+1)) + G_THROW( ERR_MSG("GIFFManager.garbage") ); +// pos=atoi((const char *)chunk_name.substr(obracket+1,cbracket-obracket-1)); + pos = chunk_name.substr(obracket+1,cbracket-obracket-1).toInt(); + chunk_name=chunk_name.substr(0,obracket); + } + DEBUG_MSG("Creating new chunk with name " << chunk_name << "\n"); + GP<GIFFChunk> chunk; + chunk=GIFFChunk::create(chunk_name, data); + add_chunk(name, chunk, pos); +} + +void +GIFFManager::del_chunk(void) +{ + DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk\n"); + DEBUG_MAKE_INDENT(3); + + G_THROW( ERR_MSG("GIFFManager.del_empty") ); +} + +void +GIFFManager::del_chunk(GUTF8String name) + // "name" should be fully qualified, that is contain dots. + // It may also end with [] to set the chunk order number +{ + DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!name.length()) + G_THROW( ERR_MSG("GIFFManager.del_empty") ); + + if (name[0]=='.') + { + const int next_dot=name.search('.',1); + if (next_dot < 0) + { + if (top_level->check_name(name.substr(1,(unsigned int)-1))) + { + DEBUG_MSG("Removing top level chunk..\n"); + top_level=GIFFChunk::create(); + return; + } + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1)); + } + const GUTF8String top_name=name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name); + name=name.substr(next_dot+1,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *)name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start && *end=='.') + cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start)); + if (!cur_sec) + G_THROW( ERR_MSG("GIFFManager.cant_find") "\t"+GUTF8String(name)); + } while(*end); + + if (!start[0]) + { + G_THROW(GUTF8String( ERR_MSG("GIFFManager.malformed") "\t")+name); + } + + cur_sec->del_chunk(start); +} + +GP<GIFFChunk> +GIFFManager::get_chunk(GUTF8String name, int * pos_num) + // "name" should be fully qualified, that is contain dots. + // It may also end with [] to set the chunk order number +{ + DEBUG_MSG("GIFFManager::get_chunk(): Returning chunk '" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + if (!name.length()) + G_THROW( ERR_MSG("GIFFManager.get_empty") ); + + if (name[0]=='.') + { + const int next_dot=name.search('.',1); + if (next_dot < 0) + { + if (top_level->check_name(name.substr(1,(unsigned int)-1))) + { + DEBUG_MSG("Removing top level chunk..\n"); + return top_level; + } + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1)); + } + const GUTF8String top_name=name.substr(1,next_dot-1); + if (!top_level->check_name(top_name)) + G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name); + name=name.substr(next_dot+1,(unsigned int)-1); + } + + GP<GIFFChunk> cur_sec=top_level; + const char * start, * end=(const char *) name-1; + do + { + for(start=++end;*end&&(*end!='.');end++) + EMPTY_LOOP; + if (end>start) + cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start), pos_num); + if (!cur_sec) + break; + } while(*end); + + return cur_sec; +} + +int +GIFFManager::get_chunks_number(void) +{ + DEBUG_MSG("GIFFManager::get_chunks_number()\n"); + DEBUG_MAKE_INDENT(3); + return top_level->get_chunks_number(); +} + +int +GIFFManager::get_chunks_number(const GUTF8String &name) + // Returns the number of chunks with given fully qualified name +{ + DEBUG_MSG("GIFFManager::get_chunks_number(): name='" << name << "'\n"); + DEBUG_MAKE_INDENT(3); + + int retval; + const int last_dot=name.rsearch('.'); + if (last_dot<0) + { + retval=top_level->get_chunks_number(name); + }else if(!last_dot) + { + retval=(top_level->get_name()==name.substr(1,(unsigned int)-1))?1:0; + }else + { + GP<GIFFChunk> chunk=get_chunk(name.substr(0,last_dot)); + retval=( chunk + ?(chunk->get_chunks_number(name.substr(last_dot+1,(unsigned int)-1))) + :0 ); + } + return retval; +} + +void +GIFFManager::load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk) +{ + DEBUG_MSG("GIFFManager::load_chunk(): loading contents of chunk '" << + chunk->get_name() << "'\n"); + DEBUG_MAKE_INDENT(3); + + int chunk_size; + GUTF8String chunk_id; + while ((chunk_size=istr.get_chunk(chunk_id))) + { + if (istr.check_id(chunk_id)) + { + GP<GIFFChunk> ch=GIFFChunk::create(chunk_id); + load_chunk(istr, ch); + chunk->add_chunk(ch); + } else + { + TArray<char> data(chunk_size-1); + istr.get_bytestream()->readall( (char*)data, data.size()); + GP<GIFFChunk> ch=GIFFChunk::create(chunk_id, data); + chunk->add_chunk(ch); + } + istr.close_chunk(); + } +} + +void +GIFFManager::load_file(const TArray<char> & data) +{ + GP<ByteStream> str=ByteStream::create((const char *)data, data.size()); + load_file(str); +} + +void +GIFFManager::load_file(GP<ByteStream> str) +{ + DEBUG_MSG("GIFFManager::load_file(): Loading IFF file.\n"); + DEBUG_MAKE_INDENT(3); + + GP<IFFByteStream> gistr=IFFByteStream::create(str); + IFFByteStream &istr=*gistr; + GUTF8String chunk_id; + if (istr.get_chunk(chunk_id)) + { + if (chunk_id.substr(0,5) != "FORM:") + G_THROW( ERR_MSG("GIFFManager.cant_find2") ); + set_name(chunk_id); + load_chunk(istr, top_level); + istr.close_chunk(); + } +} + +void +GIFFManager::save_file(TArray<char> & data) +{ + GP<ByteStream> gstr=ByteStream::create(); + save_file(gstr); + data=gstr->get_data(); +} + +void +GIFFManager::save_file(GP<ByteStream> str) +{ + GP<IFFByteStream> istr=IFFByteStream::create(str); + top_level->save(*istr, 1); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GIFFManager.h b/kviewshell/plugins/djvu/libdjvu/GIFFManager.h new file mode 100644 index 00000000..722f592f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GIFFManager.h @@ -0,0 +1,394 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GIFFManager.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GIFFMANAGER_H +#define _GIFFMANAGER_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "IFFByteStream.h" +#include "GContainer.h" +#include "Arrays.h" +#include "GSmartPointer.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GIFFManager.h + + Files #"GIFFManager.h"# and #"GIFFManager.cpp"# define more convenient + interface to IFF files. You may want to use the {\Ref GIFFManager} class + instead of coping with {\Ref IFFByteStream} especially when you have to + insert or move chunks, which is a kind of tricky with sequential access + provided by {\Ref IFFByteStream}. + + You will mostly deal with {\Ref GIFFManager} class, but sometimes you may + want to use {\Ref GIFFChunk}s as well thus bypassing {\Ref GIFFManager}'s + interface and working with the chunks hierarchy yourself. + + Interface to IFF files. + @author + Andrei Erofeev <[email protected]> -- Initial implementation. + @version + #$Id: GIFFManager.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ + +/** #GIFFChunk# is the base class for other IFF chunks understood by + {\Ref GIFFManager}. It provides some basic interface, and is not supposed + to be used on its own. */ + +class GIFFChunk : public GPEnabled +{ +protected: + GIFFChunk(void); + GIFFChunk(const GUTF8String &name); + GIFFChunk(const GUTF8String &name, const TArray<char> & data); +public: + /// Default creator. + static GP<GIFFChunk> create(void) {return new GIFFChunk();} + + /** Creates the chunk with the given name. The {\em name} may not + contain dots colons or brackets */ + static GP<GIFFChunk> create(const GUTF8String &name) + {return new GIFFChunk(name);} + + /** Creates the {\em plain chunk} containing raw data */ + static GP<GIFFChunk> create(const GUTF8String &name, const TArray<char> & data) + { return new GIFFChunk(name,data); } + + /// Destructor + virtual ~GIFFChunk(void); + + /// Returns the name of the chunk (without possible #FORM:# or similar prefixes) + GUTF8String get_name(void) const; + /// Returns full chunk name, with possible container specification + GUTF8String get_full_name(void) const; + /// Returns the chunk type, like #CAT# for chunk #CAT:DJVU# + GUTF8String get_type(void) const; + /// Returns TRUE if the chunk may contain other chunks or FALSE otherwise + bool is_container(void) const; + /** Sets the chunk name. The {\em name} may not contain dots or brackets, + but {\bf may} contain colons. */ + void set_name(GUTF8String name); + /** Parses the {\em name} probably containing colon and compares it + with its own name returning TRUE if they are the same */ + bool check_name(GUTF8String name); + + /** Adds the {\em chunk} to the chunks list at position {\em order}. + Set {\em order} to #-1# to append the chunk to the list. + {\bf Note!} By adding chunk #PROP# you will convert this chunk + to type #LIST# {\em automatically}. */ + void add_chunk(const GP<GIFFChunk> & chunk, int order=-1); + /** Removes the chunk with given {\em name}. The {\em name} may not + contain dots, but MAY contain colons and brackets (the latter - + for specifying the chunk number) */ + void del_chunk(const GUTF8String &name); + /** Returns the chunk with given {\em name}. The {\em name} may not + contain dots, but MAY contain colons and brackets (the latter - + for specifying the chunk number). If {\em position} is not zero + then the chunk position in its parent will be put into #*position# */ + GP<GIFFChunk>get_chunk(const GUTF8String &name, int * position=0); + /** Returns the number of chunks with given {\em name}. The {\em name} + may not contain dots and brackets. If {\em name} is ZERO, the + total number of chunks will be returned. */ + int get_chunks_number(const GUTF8String &name); + int get_chunks_number(void); + /** Returns the data array for plain chunks */ + TArray<char> get_data(void) const; + + /** Saves the chunk into the {\Ref IFFByteStream}. + Set {\em use_trick} to #1# if this is a top-level chunk */ + void save(IFFByteStream & istr, bool use_trick=0); +private: + char name[5]; + GUTF8String type; + GPList<GIFFChunk> chunks; + TArray<char> data; + static GUTF8String decode_name(const GUTF8String &name, int &number); +}; + +inline GUTF8String +GIFFChunk::get_name(void) const { return GUTF8String(name, 4); } + +inline GUTF8String +GIFFChunk::get_type(void) const { return type; } + +inline GUTF8String +GIFFChunk::get_full_name(void) const { return get_type()+":"+get_name(); } + +inline bool +GIFFChunk::is_container(void) const { return type.length()!=0; } + +inline TArray<char> +GIFFChunk::get_data(void) const { return data; } + +inline +GIFFChunk::GIFFChunk(void) { name[0]=0; } + +inline +GIFFChunk::GIFFChunk(const GUTF8String &name) { set_name(name); } + +inline +GIFFChunk::GIFFChunk(const GUTF8String &name, const TArray<char> & data_in) : + data(data_in) +{ + set_name(name); +} + +//************************************************************************ + +/** Intuitive interface to IFF files. + + It's too terrible to keep reading/writing IFF files chunk after chunk + using {\Ref IFFByteStream}s. This class allows you to operate with chunks + as with structures or arrays without even caring about the byte streams. + + Some of the examples are below: + \begin{verbatim} + GP<GIFFChunk> chunk; + chunk=manager1.get_chunk("BG44[2]"); + manager2.add_chunk(".FORM:DJVU.BG44[-1]", chunk); + \end{verbatim} + + {\bf Chunk name} + \begin{itemize} + \item Every chunk name may contain optional prefix #FORM:#, #LIST:#, + #PROP:# or #CAT:#. If the prefix is omitted and the chunk happens + to contain other chunks, #FORM:# will be assumed. + \item Every chunk name may be {\em short} or {\em complete}. + {\em short} chunk names may not contain dots as they're a + subchunks names with respect to a given chunk. + {\em complete} chunk names may contain dots. But there may be + or may not be the {\em leading dot} in the name. If the + {\em leading dot} is present, then the name is assumed to contain + the name of the top-level chunk as well. Otherwise it's treated + {\em with respect} to the top-level chunk. You may want to use + the leading dot only when you add a chunk to an empty document, + since a command like #manager.addChunk(".FORM:DJVU.BG44", chunk)# + will create the top level chunk of the requested type (#FORM:DJVU#) + and will add chunk #BG44# to it {\em automatically}. + \item You may use {\em brackets} in the name to specify the chunk's + position. The meaning of the number inside the brackets depends + on the function you call. In most of the cases this is the number + of the chunk with the given name in the parent chunk. But sometimes + (as in #addChunk(name, buffer, length)#) the brackets at the + end of the #name# actually specify the {\em position} of the + chunk in the parent. For example, to insert #INCL# chunk into + #DJVU# form at position #1# (make it the second) you may want to + use #manager.addChunk(".DJVU.INCL[1]", data, size)#. At the same + time, to get 2-nd chunk with name #BG44# from form #DJVU# you + should do smth like #chunk=manager.getChunk("BG44[1]")#. Note, that + here the manager will search for chunk #BG44# in form #DJVU# and + will take the second {\em found} one. + \end{itemize} */ + +class GIFFManager : public GPEnabled +{ +protected: + GIFFManager(void); + void init(void); + void init(const GUTF8String &name); +public: + /// Default creator. + static GP<GIFFManager> create(void); + + /** Creates the {\Ref GIFFManager} and assigns name {\em name} to + the top-level chunk. you may use chunk type names (before colon) + to set the top-level chunk type, or omit it to work with #FORM# */ + static GP<GIFFManager> create(const GUTF8String &name); + + /// Virtual destructor. + virtual ~GIFFManager(void); + + /// Sets the name of the top level chunk to {\em name} + void set_name(const GUTF8String &name); + /** Adds the chunk {\em chunk} to chunk with name {\em parent_name} at + position {\em pos}. {\em parent_name} may contain dots, brackets + and colons. All missing chunks in the chain will be created. + + {\bf Examples:} + \begin{verbatim} + ;; To set the top-level chunk to 'ch' + m.addChunk(".", ch); + ;; To add 'ch' to the top-level chunk "DJVU" creating it if necessary + m.addChunk(".DJVU", ch); + ;; Same as above regardless of top-level chunk name + m.addChunk("", ch); + ;; To add 'ch' to 2nd FORM DJVU in top-level form DJVM + m.addChunk(".FORM:DJVM.FORM:DJVU[1]", ch); + ;; Same thing regardless of the top-level chunk name + m.addChunk("FORM:DJVU[1]", ch); + \end{verbatim} */ + void add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk, int pos=-1); + /** If {\em name}={\em name1}.{\em name2} where {\em name2} doesn't + contain dots, then #addChunk()# will create plain chunk with + name {\em name2} with data {\em buffer} of size {\em length} and + will add it to chunk {\em name1} in the same way as + #addChunk(name, chunk, pos)# function would do it. The #pos# in + this case is either #-1# (append) or is extracted from between + brackets if the {\em name} ends with them. + + {\bf Examples:} + \begin{verbatim} + ;; To insert INCL chunk at position 2 (make it 3rd) + m.addChunk("INCL[2]", data, length); + ;; To append chunk BG44 to 2nd DjVu file inside DjVm archive: + m.addChunk(".DJVM.DJVU[1].BG44", data, length); + \end{verbatim} */ + void add_chunk(GUTF8String name, const TArray<char> & data); + /** Will remove chunk with name {\em name}. You may use dots, colons + and brackets to specify the chunk uniquely. + + {\bf Examples:} + \begin{verbatim} + ;; To remove 2nd DjVu document from DjVm archive use + m.delChunk(".DJVM.DJVU[1]"); + ;; Same thing without top-level chunk name specification + m.delChunk("DJVU[1]"); + ;; Same thing for the first DJVU chunk + m.delChunk("DJVU"); + \end{verbatim} + */ + void del_chunk(GUTF8String name); + void del_chunk(void); + /** Will return the number of chunks with given name. The {\em name} may + not end with brackets, but may contain them inside. It may also + contain dots and colons. If {\em name} is ZERO, the total number + of chunks will be returned. + + {\bf Examples:} + \begin{verbatim} + ;; To get the number of DJVU forms inside DjVm document + m.getChunksNumber(".DJVM.DJVU"); + ;; Same thing without top-level chunk name specification + m.getChunksNumber("DJVU"); + \end{verbatim} + */ + int get_chunks_number(const GUTF8String &name); + int get_chunks_number(void); + + /** Returns the chunk with name {\em name}. The {\em name} may contain dots + colons and slashes. If {\em position} is not zero, #*position# will + be assigned the position of the found chunk in the parent chunk. + + {\bf Examples:} + \begin{verbatim} + ;; To get the directory chunk of DjVm document + m.getChunk(".DJVM.DIR0"); + ;; To get chunk corresponding to 2nd DJVU form + m.getChunk(".DJVU[1]"); + \end{verbatim} */ + GP<GIFFChunk>get_chunk(GUTF8String name, int * position=0); + + /** Loads the composite {\em chunk}'s contents from stream {\em istr}. */ + void load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk); + /** Loads the file contents from stream {\em str} */ + void load_file(GP<ByteStream> str); + /** Loads the file contents from the data array {\em data} */ + void load_file(const TArray<char> & data); + /** Saves all the chunks into stream {\em str} */ + void save_file(GP<ByteStream> str); + /** Saves all the chunks into the data array {\em data} */ + void save_file(TArray<char> & data); + +private: + GP<GIFFChunk> top_level; + + static const char * check_leading_dot(const GUTF8String &name); +private: //dummy methods + static void save_file(ByteStream *); + static void load_file(ByteStream *); +}; + +inline void +GIFFManager::set_name(const GUTF8String &name) +{ + top_level->set_name(name); +} + +inline +GIFFManager::GIFFManager(void) {} + +inline void +GIFFManager::init(void) +{ + top_level=GIFFChunk::create(); +} + +inline void +GIFFManager::init(const GUTF8String &name) +{ + top_level=GIFFChunk::create(name); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp b/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp new file mode 100644 index 00000000..5a85e1fc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp @@ -0,0 +1,1066 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GMapAreas.cpp,v 1.9 2004/05/05 15:12:42 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GMapAreas.h" +#include "GException.h" +#include "debug.h" + +#include <math.h> +#include <stdio.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/**************************************************************************** +***************************** GMapArea definition *************************** +****************************************************************************/ + +const char GMapArea::MAPAREA_TAG[] = "maparea"; +const char GMapArea::RECT_TAG[] = "rect"; +const char GMapArea::POLY_TAG[] = "poly"; +const char GMapArea::OVAL_TAG[] = "oval"; +const char GMapArea::NO_BORDER_TAG[] = "none"; +const char GMapArea::XOR_BORDER_TAG[] = "xor"; +const char GMapArea::SOLID_BORDER_TAG[] = "border"; +const char GMapArea::SHADOW_IN_BORDER_TAG[] = "shadow_in"; +const char GMapArea::SHADOW_OUT_BORDER_TAG[] = "shadow_out"; +const char GMapArea::SHADOW_EIN_BORDER_TAG[] = "shadow_ein"; +const char GMapArea::SHADOW_EOUT_BORDER_TAG[] = "shadow_eout"; +const char GMapArea::BORDER_AVIS_TAG[] = "border_avis"; +const char GMapArea::HILITE_TAG[] = "hilite"; +const char GMapArea::URL_TAG[] = "url"; +const char GMapArea::TARGET_SELF[] = "_self"; +static const char zero_width[] = ERR_MSG("GMapAreas.zero_width"); +static const char zero_height[] = ERR_MSG("GMapAreas.zero_height"); +static const char width_1[] = ERR_MSG("GMapAreas.width_1"); +static const char width_3_32 [] = ERR_MSG("GMapAreas.width_3-32"); +static const char error_poly_border [] = ERR_MSG("GMapAreas.poly_border"); +static const char error_poly_hilite [] = ERR_MSG("GMapAreas.poly_hilite"); +static const char error_oval_border [] = ERR_MSG("GMapAreas.oval_border"); +static const char error_oval_hilite [] = ERR_MSG("GMapAreas.oval_hilite"); +static const char error_too_few_points [] = ERR_MSG("GMapAreas.too_few_points"); +static const char error_intersect [] = ERR_MSG("GMapAreas.intersect"); + +GMapArea::~GMapArea() {} + +GMapRect::~GMapRect() {} + +GMapPoly::~GMapPoly() {} + +GMapOval::~GMapOval() {} + +void +GMapArea::initialize_bounds(void) +{ + xmin=gma_get_xmin(); + xmax=gma_get_xmax(); + ymin=gma_get_ymin(); + ymax=gma_get_ymax(); + bounds_initialized=true; +} + +int +GMapArea::get_xmin(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return xmin; +} + +int +GMapArea::get_ymin(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return ymin; +} + +int +GMapArea::get_xmax(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return xmax; +} + +int +GMapArea::get_ymax(void) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return ymax; +} + +GRect +GMapArea::get_bound_rect(void) const +{ + return GRect(get_xmin(), get_ymin(), get_xmax()-get_xmin(), + get_ymax()-get_ymin()); +} + +void +GMapArea::move(int dx, int dy) +{ + if (dx || dy) + { + if (bounds_initialized) + { + xmin+=dx; + ymin+=dy; + xmax+=dx; + ymax+=dy; + } + gma_move(dx, dy); + } +} + +void +GMapArea::resize(int new_width, int new_height) +{ + if (get_xmax()-get_xmin()!=new_width || + get_ymax()-get_ymin()!=new_height) + { + gma_resize(new_width, new_height); + bounds_initialized=false; + } +} + +void +GMapArea::transform(const GRect & grect) +{ + if (grect.xmin!=get_xmin() || grect.ymin!=get_ymin() || + grect.xmax!=get_xmax() || grect.ymax!=get_ymax()) + { + gma_transform(grect); + bounds_initialized=false; + } +} + +char const * const +GMapArea::check_object(void) +{ + char const *retval; + if (get_xmax()==get_xmin()) + { + retval=zero_width; + } + else if (get_ymax()==get_ymin()) + { + retval=zero_height; + } + else if ((border_type==XOR_BORDER || + border_type==SOLID_BORDER) && border_width!=1) + { + retval=width_1; + } + else if ((border_type==SHADOW_IN_BORDER || + border_type==SHADOW_OUT_BORDER || + border_type==SHADOW_EIN_BORDER || + border_type==SHADOW_EOUT_BORDER)&& + (border_width<3 || border_width>32)) + { + retval=width_3_32; + }else + { + retval=gma_check_object(); + } + return retval; +} + +bool +GMapArea::is_point_inside(int x, int y) const +{ + if (!bounds_initialized) + const_cast<GMapArea *>(this)->initialize_bounds(); + return (x>=xmin && x<xmax && y>=ymin && y<ymax) ? + gma_is_point_inside(x, y) : false; +} + +GUTF8String +GMapArea::print(void) +{ + // Make this hard check to make sure, that *no* illegal GMapArea + // can be stored into a file. + const char * const errors=check_object(); + if (errors[0]) + { + G_THROW(errors); + } + + int i; + GUTF8String tmp; + GUTF8String url1, target1, comment1; + const GUTF8String url_str=url; + for(i=0;i<(int) url_str.length();i++) + { + char ch=url_str[i]; + if (ch=='"') + url1+='\\'; + url1+=ch; + } + for(i=0;i<(int) target.length();i++) + { + char ch=target[i]; + if (ch=='"') + target1+='\\'; + target1+=ch; + } + for(i=0;i<(int) comment.length();i++) + { + char ch=comment[i]; + if (ch=='"') + comment1+='\\'; + comment1+=ch; + } + + GUTF8String border_color_str; + border_color_str.format("#%02X%02X%02X", + (border_color & 0xff0000) >> 16, + (border_color & 0xff00) >> 8, + (border_color & 0xff)); + + static const GUTF8String left('('); + static const GUTF8String right(')'); + static const GUTF8String space(' '); + static const GUTF8String quote('"'); + GUTF8String border_type_str; + switch(border_type) + { + case NO_BORDER: + border_type_str=left+NO_BORDER_TAG+right; + break; + case XOR_BORDER: + border_type_str=left+XOR_BORDER_TAG+right; + break; + case SOLID_BORDER: + border_type_str=left+SOLID_BORDER_TAG+space+border_color_str+right; + break; + case SHADOW_IN_BORDER: + border_type_str=left+SHADOW_IN_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_OUT_BORDER: + border_type_str=left+SHADOW_OUT_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_EIN_BORDER: + border_type_str=left+SHADOW_EIN_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + case SHADOW_EOUT_BORDER: + border_type_str=left+SHADOW_EOUT_BORDER_TAG+space+GUTF8String(border_width)+right; + break; + default: + border_type_str=left+XOR_BORDER_TAG+right; + break; + } + + GUTF8String hilite_str; + if (hilite_color!=0xffffffff) + { + hilite_str.format("(%s #%02X%02X%02X)", + HILITE_TAG, (hilite_color & 0xff0000) >> 16, + (hilite_color & 0xff00) >> 8, + (hilite_color & 0xff)); + } + + GUTF8String URL; + if (target1==TARGET_SELF) + { + URL=quote+url1+quote; + }else + { + URL=left+URL_TAG+space+quote+url1+quote+space+quote+target1+quote+right; + } + + GUTF8String total=left+MAPAREA_TAG+space+URL+space+quote+comment1+quote+space+gma_print()+border_type_str; + if (border_always_visible) + total+=space+left+BORDER_AVIS_TAG+right; + if ( hilite_str.length() > 0 ) + total+=space+hilite_str; + total+=right; + return total; +} + +/* +void +GMapArea::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect = GRect(xmin, ymin, xmax, ymax); + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +void +GMapArea::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect = GRect(xmin, ymin, xmax, ymax); + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +*/ + + +/// Virtual function generating a list of defining coordinates +/// (default are the opposite corners of the enclosing rectangle) +void GMapArea::get_coords( GList<int> & CoordList ) const +{ + CoordList.append( get_xmin() ); + CoordList.append( get_ymin() ); + CoordList.append( get_xmax() ); + CoordList.append( get_ymax() ); +} + + +/**************************************************************************** +**************************** GMapRect definition **************************** +****************************************************************************/ + +void +GMapRect::gma_resize(int new_width, int new_height) +{ + xmax=xmin+new_width; + ymax=ymin+new_height; +} + +void +GMapRect::gma_transform(const GRect & grect) +{ + xmin=grect.xmin; ymin=grect.ymin; + xmax=grect.xmax; ymax=grect.ymax; +} + +GUTF8String +GMapRect::gma_print(void) +{ + GUTF8String buffer; + return buffer.format("(%s %d %d %d %d) ", + RECT_TAG, xmin, ymin, xmax-xmin, ymax-ymin); +} + +void +GMapRect::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} +void +GMapRect::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); +} + +/**************************************************************************** +**************************** GMapPoly definition **************************** +****************************************************************************/ + +inline int +GMapPoly::sign(int x) { return x<0 ? -1 : x>0 ? 1 : 0; } + +bool +GMapPoly::does_side_cross_rect(const GRect & grect, int side) +{ + int x1=xx[side], x2=xx[(side+1)%points]; + int y1=yy[side], y2=yy[(side+1)%points]; + int xmin=x1<x2 ? x1 : x2; + int ymin=y1<y2 ? y1 : y2; + int xmax=x1+x2-xmin; + int ymax=y1+y2-ymin; + + if (xmax<grect.xmin || xmin>grect.xmax || + ymax<grect.ymin || ymin>grect.ymax) return false; + + return + x1>=grect.xmin && x1<=grect.xmax && y1>=grect.ymin && y1<=grect.ymax || + x2>=grect.xmin && x2<=grect.xmax && y2>=grect.ymin && y2<=grect.ymax || + do_segments_intersect(grect.xmin, grect.ymin, grect.xmax, grect.ymax, + x1, y1, x2, y2) || + do_segments_intersect(grect.xmax, grect.ymin, grect.xmin, grect.ymax, + x1, y1, x2, y2); +} + +bool +GMapPoly::is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2) +{ + int res1=(x-x1)*(x2-x1)+(y-y1)*(y2-y1); + int res2=(x-x2)*(x2-x1)+(y-y2)*(y2-y1); + return sign(res1)*sign(res2)<=0; +} + +bool +GMapPoly::do_segments_intersect(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22) +{ + int res11=(x11-x21)*(y22-y21)-(y11-y21)*(x22-x21); + int res12=(x12-x21)*(y22-y21)-(y12-y21)*(x22-x21); + int res21=(x21-x11)*(y12-y11)-(y21-y11)*(x12-x11); + int res22=(x22-x11)*(y12-y11)-(y22-y11)*(x12-x11); + if (!res11 && !res12) + { + // Segments are on the same line + return + is_projection_on_segment(x11, y11, x21, y21, x22, y22) || + is_projection_on_segment(x12, y12, x21, y21, x22, y22) || + is_projection_on_segment(x21, y21, x11, y11, x12, y12) || + is_projection_on_segment(x22, y22, x11, y11, x12, y12); + } + int sign1=sign(res11)*sign(res12); + int sign2=sign(res21)*sign(res22); + return sign1<=0 && sign2<=0; +} + +bool +GMapPoly::are_segments_parallel(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22) +{ + return (x12-x11)*(y22-y21)-(y12-y11)*(x22-x21)==0; +} + +char const * const +GMapPoly::check_data(void) +{ + if (open && points<2 || !open && points<3) + return error_too_few_points; + for(int i=0;i<sides;i++) + { + for(int j=i+2;j<sides;j++) + { + if (i != (j+1)%points ) + { + if (do_segments_intersect(xx[i], yy[i], xx[i+1], yy[i+1], + xx[j], yy[j], xx[(j+1)%points], yy[(j+1)%points])) + { + return error_intersect; + } + } + } + } + return ""; +} + +void +GMapPoly::optimize_data(void) +{ + // Removing segments of length zero + int i; + for(i=0;i<sides;i++) + { + while(xx[i]==xx[(i+1)%points] && yy[i]==yy[(i+1)%points]) + { + for(int k=(i+1)%points;k<points-1;k++) + { + xx[k]=xx[k+1]; yy[k]=yy[k+1]; + } + points--; sides--; + if (!points) return; + } + } + // Concatenating consequitive parallel segments + for(i=0;i<sides;i++) + { + while((open && i+1<sides || !open) && + are_segments_parallel(xx[i], yy[i], + xx[(i+1)%points], yy[(i+1)%points], + xx[(i+1)%points], yy[(i+1)%points], + xx[(i+2)%points], yy[(i+2)%points])) + { + for(int k=(i+1)%points;k<points-1;k++) + { + xx[k]=xx[k+1]; yy[k]=yy[k+1]; + } + points--; sides--; + if (!points) return; + } + } +} + +bool +GMapPoly::gma_is_point_inside(const int xin, const int yin) const +{ + if (open) + return false; + + int xfar=get_xmax()+(get_xmax()-get_xmin()); + + int intersections=0; + for(int i=0;i<points;i++) + { + int res1=yy[i]-yin; + if (!res1) continue; + int res2, isaved=i; + while(!(res2=yy[(i+1)%points]-yin)) i++; + if (isaved!=i) + { + // Some points fell exactly on the line + if ((xx[(isaved+1)%points]-xin)* + (xx[i%points]-xin)<=0) + { + // Test point is exactly on the boundary + return true; + } + } + if (res1<0 && res2>0 || res1>0 && res2<0) + { + int x1=xx[i%points], y1=yy[i%points]; + int x2=xx[(i+1)%points], y2=yy[(i+1)%points]; + int _res1=(xin-x1)*(y2-y1)-(yin-y1)*(x2-x1); + int _res2=(xfar-x1)*(y2-y1)-(yin-y1)*(x2-x1); + if (!_res1 || !_res2) + { + // The point is on this boundary + return true; + } + if (sign(_res1)*sign(_res2)<0) intersections++; + } + } + return (intersections % 2)!=0; +} + +int +GMapPoly::gma_get_xmin(void) const +{ + int x=xx[0]; + for(int i=1;i<points;i++) + if (x>xx[i]) x=xx[i]; + return x; +} + +int +GMapPoly::gma_get_xmax(void) const +{ + int x=xx[0]; + for(int i=1;i<points;i++) + if (x<xx[i]) x=xx[i]; + return x+1; +} + +int +GMapPoly::gma_get_ymin(void) const +{ + int y=yy[0]; + for(int i=1;i<points;i++) + if (y>yy[i]) y=yy[i]; + return y; +} + +int +GMapPoly::gma_get_ymax(void) const +{ + int y=yy[0]; + for(int i=1;i<points;i++) + if (y<yy[i]) y=yy[i]; + return y+1; +} + +void +GMapPoly::gma_move(int dx, int dy) +{ + for(int i=0;i<points;i++) + { + xx[i]+=dx; yy[i]+=dy; + } +} + +void +GMapPoly::gma_resize(int new_width, int new_height) +{ + int width=get_xmax()-get_xmin(); + int height=get_ymax()-get_ymin(); + int xmin=get_xmin(), ymin=get_ymin(); + for(int i=0;i<points;i++) + { + xx[i]=xmin+(xx[i]-xmin)*new_width/width; + yy[i]=ymin+(yy[i]-ymin)*new_height/height; + } +} + +void +GMapPoly::gma_transform(const GRect & grect) +{ + int width=get_xmax()-get_xmin(); + int height=get_ymax()-get_ymin(); + int xmin=get_xmin(), ymin=get_ymin(); + for(int i=0;i<points;i++) + { + xx[i]=grect.xmin+(xx[i]-xmin)*grect.width()/width; + yy[i]=grect.ymin+(yy[i]-ymin)*grect.height()/height; + } +} + +char const * const +GMapPoly::gma_check_object(void) const +{ + const char * str; + str=(border_type!=NO_BORDER && + border_type!=SOLID_BORDER && + border_type!=XOR_BORDER) ? error_poly_border: + ((hilite_color!=0xffffffff) ? error_poly_hilite:""); + return str; +} + +GMapPoly::GMapPoly(const int * _xx, const int * _yy, int _points, bool _open) : + open(_open), points(_points) +{ + sides=points-(open!=0); + + xx.resize(points-1); yy.resize(points-1); + for(int i=0;i<points;i++) + { + xx[i]=_xx[i]; yy[i]=_yy[i]; + } + optimize_data(); + char const * const res=check_data(); + if (res[0]) + G_THROW(res); +} + +int +GMapPoly::add_vertex(int x, int y) +{ + points++; + sides=points-(open!=0); + + xx.resize(points-1); yy.resize(points-1); + xx[points-1] = x; + yy[points-1] = y; + + return points; +} + +void +GMapPoly::close_poly() +{ + open = false; + sides=points; +} + +GUTF8String +GMapPoly::gma_print(void) +{ + static const GUTF8String space(' '); + GUTF8String res=GUTF8String('(')+POLY_TAG+space; + for(int i=0;i<points;i++) + { + GUTF8String buffer; + res+=buffer.format("%d %d ", xx[i], yy[i]); + } + res.setat(res.length()-1, ')'); + res+=space; + return res; +} + +/// Virtual function generating a list of defining coordinates +void GMapPoly::get_coords( GList<int> & CoordList ) const +{ + for(int i = 0 ; i < points ; i++) + { + CoordList.append( xx[i] ); + CoordList.append( yy[i] ); + } +} + +void +GMapPoly::map(GRectMapper &mapper) +{ + get_bound_rect(); + for(int i=0; i<points; i++) + { + mapper.map(xx[i], yy[i]); + } + clear_bounds(); +} + +void +GMapPoly::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + for(int i=0; i<points; i++) + { + mapper.unmap(xx[i], yy[i]); + } + clear_bounds(); +} + + + +/**************************************************************************** +**************************** GMapOval definition **************************** +****************************************************************************/ + +void +GMapOval::gma_resize(int new_width, int new_height) +{ + xmax=xmin+new_width; + ymax=ymin+new_height; + initialize(); +} + +void +GMapOval::gma_transform(const GRect & grect) +{ + xmin=grect.xmin; ymin=grect.ymin; + xmax=grect.xmax; ymax=grect.ymax; + initialize(); +} + +bool +GMapOval::gma_is_point_inside(const int x, const int y) const +{ + return + sqrt((double)((x-xf1)*(x-xf1)+(y-yf1)*(y-yf1))) + + sqrt((double)((x-xf2)*(x-xf2)+(y-yf2)*(y-yf2))) <= 2*rmax; +} + +char const * const +GMapOval::gma_check_object(void) const +{ + return (border_type!=NO_BORDER && + border_type!=SOLID_BORDER && + border_type!=XOR_BORDER)?error_oval_border: + ((hilite_color!=0xffffffff) ? error_oval_hilite:""); +} + +void +GMapOval::initialize(void) +{ + int xc=(xmax+xmin)/2; + int yc=(ymax+ymin)/2; + int f; + + a=(xmax-xmin)/2; + b=(ymax-ymin)/2; + if (a>b) + { + rmin=b; rmax=a; + f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); + xf1=xc+f; xf2=xc-f; yf1=yf2=yc; + } else + { + rmin=a; rmax=b; + f=(int) sqrt((double)(rmax*rmax-rmin*rmin)); + yf1=yc+f; yf2=yc-f; xf1=xf2=xc; + } +} + +GMapOval::GMapOval(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), + xmax(rect.xmax), ymax(rect.ymax) +{ + initialize(); +} + +GUTF8String +GMapOval::gma_print(void) +{ + GUTF8String buffer; + return buffer.format("(%s %d %d %d %d) ", + OVAL_TAG, xmin, ymin, xmax-xmin, ymax-ymin); +} + +void +GMapOval::map(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.map(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); + initialize(); +} + +void +GMapOval::unmap(GRectMapper &mapper) +{ + get_bound_rect(); + GRect rect; + rect.xmin = xmin; + rect.xmax = xmax; + rect.ymin = ymin; + rect.ymax = ymax; + mapper.unmap(rect); + xmin = rect.xmin; + ymin = rect.ymin; + xmax = rect.xmax; + ymax = rect.ymax; + clear_bounds(); + initialize(); +} + +GMapArea::GMapArea(void) : target("_self"), border_type(NO_BORDER), + border_always_visible(false), border_color(0xff), border_width(1), + hilite_color(0xffffffff), bounds_initialized(0) {} + +GMapRect::GMapRect(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} + +GMapRect::GMapRect(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin), + xmax(rect.xmax), ymax(rect.ymax) {} + +GMapRect & +GMapRect::operator=(const GRect & rect) +{ + xmin=rect.xmin; + xmax=rect.xmax; + ymin=rect.ymin; + ymax=rect.ymax; + return *this; +} + +void +GMapRect::gma_move(int dx, int dy) +{ + xmin+=dx; + xmax+=dx; + ymin+=dy; + ymax+=dy; +} + +bool +GMapRect::gma_is_point_inside(const int x, const int y) const +{ + return (x>=xmin)&&(x<xmax)&&(y>=ymin)&&(y<ymax); +} + +GP<GMapArea> +GMapRect::get_copy(void) const { return new GMapRect(*this); } + +GMapPoly::GMapPoly(void) : points(0), sides(0) {} + +void +GMapPoly::move_vertex(int i, int x, int y) +{ + xx[i]=x; yy[i]=y; + clear_bounds(); +} + +GP<GMapArea> +GMapPoly::get_copy(void) const { return new GMapPoly(*this); } + +GMapOval::GMapOval(void) : xmin(0), ymin(0), xmax(0), ymax(0) {} + +void +GMapOval::gma_move(int dx, int dy) +{ + xmin+=dx; xmax+=dx; ymin+=dy; ymax+=dy; + xf1+=dx; yf1+=dy; xf2+=dx; yf2+=dy; +} + +GP<GMapArea> +GMapOval::get_copy(void) const +{ + return new GMapOval(*this); +} + +static GUTF8String +GMapArea2xmltag(const GMapArea &area,const GUTF8String &coords) +{ + GUTF8String retval("<AREA coords=\"" + +coords+"\" shape=\""+area.get_shape_name()+"\" " + +"alt=\""+area.comment.toEscaped()+"\" "); + if(area.url.length()) + { + retval+="href=\""+area.url+"\" "; + }else + { + retval+="nohref=\"nohref\" "; + } + if(area.target.length()) + { + retval+="target=\""+area.target.toEscaped()+"\" "; + } + // highlight + if( area.hilite_color != GMapArea::NO_HILITE && + area.hilite_color != GMapArea::XOR_HILITE ) + { + retval+=GUTF8String().format( "highlight=\"#%06X\" ", area.hilite_color ); + } + const char *b_type="none"; + switch( area.border_type ) + { + case GMapArea::NO_BORDER: + b_type = "none"; + break; + case GMapArea::XOR_BORDER: + b_type = "xor"; + break; + case GMapArea::SOLID_BORDER: + b_type = "solid"; + break; + case GMapArea::SHADOW_IN_BORDER: + b_type = "shadowin"; + break; + case GMapArea::SHADOW_OUT_BORDER: + b_type = "shadowout"; + break; + case GMapArea::SHADOW_EIN_BORDER: + b_type = "etchedin"; + break; + case GMapArea::SHADOW_EOUT_BORDER: + b_type = "etchedout"; + break; + } + retval=retval+"bordertype=\""+b_type+"\" "; + if( area.border_type != GMapArea::NO_BORDER) + { + retval+="bordercolor=\""+GUTF8String().format("#%06X",area.border_color) + +"\" border=\""+GUTF8String(area.border_width)+"\" "; + } + if(area.border_always_visible ) + retval=retval+"visible=\"visible\" "; + return retval+"/>\n"; +} + +GUTF8String +GMapRect::get_xmltag(const int height) const +{ + return GMapArea2xmltag( *this, GUTF8String(get_xmin()) + +","+GUTF8String(height-1-get_ymax()) + +","+GUTF8String(get_xmax()) + +","+GUTF8String(height-1-get_ymin())); +#if 0 + GUTF8String retval; + return retval; +#endif +} + +GUTF8String +GMapOval::get_xmltag(const int height) const +{ + return GMapArea2xmltag( *this, GUTF8String(get_xmin()) + +","+GUTF8String(height-1-get_ymax()) + +","+GUTF8String(get_xmax()) + +","+GUTF8String(height-1-get_ymin())); +#if 0 + GUTF8String retval; + return retval; +#endif +} + +GUTF8String +GMapPoly::get_xmltag(const int height) const +{ + GList<int> CoordList; + get_coords(CoordList); + GPosition pos=CoordList; + GUTF8String retval; + if(pos) + { + GUTF8String coords(CoordList[pos]); + while(++pos) + { + coords+=","+GUTF8String(height-1-CoordList[pos]); + if(! ++pos) + break; + coords+=","+GUTF8String(CoordList[pos]); + } + retval=GMapArea2xmltag( *this, coords); + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GMapAreas.h b/kviewshell/plugins/djvu/libdjvu/GMapAreas.h new file mode 100644 index 00000000..251427ed --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GMapAreas.h @@ -0,0 +1,565 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GMapAreas.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GMAPAREAS_H +#define _GMAPAREAS_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" +#include "GContainer.h" +#include "GString.h" +#include "GRect.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GMapAreas.h + + Files #"GMapAreas.h"# and #"GMapAreas.cpp"# implement base objects + used by the plugin to display and manage hyperlinks and highlighted + areas inside a \Ref{DjVuImage} page. + + The currently supported areas can be rectangular (\Ref{GMapRect}), + elliptical (\Ref{GMapOval}) and polygonal (\Ref{GMapPoly}). Every + map area besides the definition of its shape contains information + about display style and optional {\bf URL}, which it may refer to. + If this {\bf URL} is not empty then the map area will work like a + hyperlink. + + The classes also implement some useful functions to ease geometry + manipulations + + @memo Definition of base map area classes + @author Andrei Erofeev <[email protected]> + @version + #$Id: GMapAreas.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +// ---------- GMAPAREA --------- + +/** This is the base object for all map areas. It defines some standard + interface to access the geometrical properties of the areas and + describes the area itsef: + \begin{itemize} + \item #url# If the optional #URL# is specified, the map area will + also work as a hyperlink meaning that if you click it with + your mouse pointer, the browser will be advised to load + the page referenced by the #URL#. + \item #target# Defines where the specified #URL# should be loaded + \item #comment# This is a string displayed in a status line or in + a popup window when the mouse pointer moves over the hyperlink + area + \item #border_type#, #border_color# and #border_width# describes + how the area border should be drawn + \item #area_color# describes how the area should be highlighted. + \end{itemize} + + The map areas can be displayed using two different techniques, which + can be combined together: + \begin{itemize} + \item Visible border. The border of a map area can be drawn in several + different ways (like #XOR_BORDER# or #SHADOW_IN_BORDER#). + It can be made always visible, or appearing only when the + mouse pointer moves over the map area. + \item Highlighted contents. Contents of rectangular map areas can + also be highlighted with some given color. + \end{itemize} +*/ + +class GMapArea : public GPEnabled +{ +protected: + GMapArea(void); +public: +// // Default creator. +// static GP<GMapArea> create(void) {return new GMapArea();} + + /// Virtual destructor. + virtual ~GMapArea(); + + static const char MAPAREA_TAG []; + static const char RECT_TAG []; + static const char POLY_TAG []; + static const char OVAL_TAG []; + static const char NO_BORDER_TAG []; + static const char XOR_BORDER_TAG []; + static const char SOLID_BORDER_TAG []; + static const char SHADOW_IN_BORDER_TAG []; + static const char SHADOW_OUT_BORDER_TAG []; + static const char SHADOW_EIN_BORDER_TAG []; + static const char SHADOW_EOUT_BORDER_TAG []; + static const char BORDER_AVIS_TAG []; + static const char HILITE_TAG []; + static const char URL_TAG []; + static const char TARGET_SELF []; + + enum BorderType { NO_BORDER=0, XOR_BORDER=1, SOLID_BORDER=2, + SHADOW_IN_BORDER=3, SHADOW_OUT_BORDER=4, + SHADOW_EIN_BORDER=5, SHADOW_EOUT_BORDER=6 }; + + enum Special_Hilite_Color{ NO_HILITE=0xFFFFFFFF, XOR_HILITE=0xFF000000}; + + // Enumeration for reporting the type of map area. "MapUnknown" is reported + // for objects of type GMapArea (there shouldn't be any). + enum MapAreaType { UNKNOWN, RECT, OVAL, POLY }; + + /** Optional URL which this map area can be associated with. + If it's not empty then clicking this map area with the mouse + will make the browser load the HTML page referenced by + this #url#. Note: This may also be a relative URL, so the + GURL class is not used. */ + GUTF8String url; + /** The target for the #URL#. Standard targets are: + \begin{itemize} + \item #_blank# - Load the link in a new blank window + \item #_self# - Load the link into the plugin window + \item #_top# - Load the link into the top-level frame + \end{itemize} */ + GUTF8String target; + /** Comment (displayed in a status line or as a popup hint when + the mouse pointer moves over the map area */ + GUTF8String comment; + /** Border type. Defines how the map area border should be drawn + \begin{itemize} + \item #NO_BORDER# - No border drawn + \item #XOR_BORDER# - The border is drawn using XOR method. + \item #SOLID_BORDER# - The border is drawn as a solid line + of a given color. + \item #SHADOW_IN_BORDER# - Supported for \Ref{GMapRect} only. + The map area area looks as if it was "pushed-in". + \item #SHADOW_OUT_BORDER# - The opposite of #SHADOW_OUT_BORDER# + \item #SHADOW_EIN_BORDER# - Also for \Ref{GMapRect} only. + Is translated as "shadow etched in" + \item #SHADOW_EOUT_BORDER# - The opposite of #SHADOW_EIN_BORDER#. + \end{itemize} */ + BorderType border_type; + /** If #TRUE#, the border will be made always visible. Otherwise + it will be drawn when the mouse moves over the map area. */ + bool border_always_visible; + /// Border color (when relevant) in #0x00RRGGBB# format + unsigned long int border_color; + /// Border width in pixels + int border_width; + /** Specified a color for highlighting the internal area of the map + area. Will work with rectangular map areas only. The color is + specified in \#00RRGGBB format. A special value of \#FFFFFFFF disables + highlighting and \#FF000000 is for XOR highlighting. */ + unsigned long int hilite_color; + + /// Returns 1 if the given point is inside the hyperlink area + bool is_point_inside(int x, int y) const; + + /// Returns xmin of the bounding rectangle + int get_xmin(void) const; + /// Returns ymin of the bounding rectangle + int get_ymin(void) const; + /** Returns xmax of the bounding rectangle. In other words, if #X# is + a coordinate of the last point in the right direction, the + function will return #X+1# */ + int get_xmax(void) const; + /** Returns xmax of the bounding rectangle. In other words, if #Y# is + a coordinate of the last point in the top direction, the + function will return #Y+1# */ + int get_ymax(void) const; + /// Returns the hyperlink bounding rectangle + GRect get_bound_rect(void) const; + /** Moves the hyperlink along the given vector. Is used by the + hyperlinks editor. */ + void move(int dx, int dy); + /** Resizes the hyperlink to fit new bounding rectangle while + keeping the (xmin, ymin) points at rest. */ + void resize(int new_width, int new_height); + /** Transforms the hyperlink to be within the specified rectangle */ + void transform(const GRect & grect); + /** Checks if the object is OK. Especially useful with \Ref{GMapPoly} + where edges may intersect. If there is a problem it returns a + string describing it. */ + char const * const check_object(void); + /** Stores the contents of the hyperlink object in a lisp-like format + for saving into #ANTa# chunk (see \Ref{DjVuAnno}) */ + GUTF8String print(void); + + virtual GUTF8String get_xmltag(const int height) const=0; + + /// Virtual function returning the shape type. + virtual MapAreaType const get_shape_type( void ) const { return UNKNOWN; }; + /// Virtual function returning the shape name. + virtual char const * const get_shape_name(void) const=0; + /// Virtual function generating a copy of this object + virtual GP<GMapArea> get_copy(void) const=0; + /// Virtual function generating a list of defining coordinates + /// (default are the opposite corners of the enclosing rectangle) + virtual void get_coords( GList<int> & CoordList ) const; + /// Virtual function maps maparea from one area to another using mapper + virtual void map(GRectMapper &mapper)=0; + /// Virtual function unmaps maparea from one area to another using mapper + virtual void unmap(GRectMapper &mapper)=0; + +protected: + virtual int gma_get_xmin(void) const=0; + virtual int gma_get_ymin(void) const=0; + virtual int gma_get_xmax(void) const=0; + virtual int gma_get_ymax(void) const=0; + virtual void gma_move(int dx, int dy)=0; + virtual void gma_resize(int new_width, int new_height)=0; + virtual void gma_transform(const GRect & grect)=0; + virtual bool gma_is_point_inside(const int x, const int y) const=0; + virtual char const * const gma_check_object(void) const=0; + virtual GUTF8String gma_print(void)=0; + + void clear_bounds(void) { bounds_initialized=0; } +private: + int xmin, xmax, ymin, ymax; + bool bounds_initialized; + + void initialize_bounds(void); +}; + +// ---------- GMAPRECT --------- + +/** Implements rectangular map areas. This is the only kind of map areas + supporting #SHADOW_IN_BORDER#, #SHADOW_OUT_BORDER#, #SHADOW_EIN_BORDER# + and #SHADOW_EOUT_BORDER# types of border and area highlighting. */ + +class GMapRect: public GMapArea +{ +protected: + GMapRect(void); + GMapRect(const GRect & rect); +public: + /// Default creator. + static GP<GMapRect> create(void) {return new GMapRect();} + /// Create with the specified GRect. + static GP<GMapRect> create(const GRect &rect) {return new GMapRect(rect);} + + virtual ~GMapRect(); + + /// Returns the width of the rectangle + int get_width(void) const { return xmax-xmin; } + /// Returns the height of the rectangle + int get_height(void) const { return ymax-ymin; } + + /// Changes the #GMapRect#'s geometry + GMapRect & operator=(const GRect & rect); + + /// Returns \Ref{GRect} describing the map area's rectangle + operator GRect(void); + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapRect + virtual MapAreaType const get_shape_type( void ) const { return RECT; }; + /// Returns #"rect"# + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the rectangle + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function maps rectangle from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps rectangle from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + int xmin, ymin, xmax, ymax; + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +}; + +// ---------- GMAPPOLY --------- + +/** Implements polygonal map areas. The only supported types of border + are #NO_BORDER#, #XOR_BORDER# and #SOLID_BORDER#. Its contents can not + be highlighted either. It's worth mentioning here that despite its + name the polygon may be open, which basically makes it a broken line. + This very specific mode is used by the hyperlink editor when creating + the polygonal hyperlink. */ + +class GMapPoly : public GMapArea +{ +protected: + GMapPoly(void); + GMapPoly(const int * xx, const int * yy, int points, bool open=false); +public: + /// Default creator + static GP<GMapPoly> create(void) {return new GMapPoly();} + + /// Create from specified coordinates. + static GP<GMapPoly> create( + const int xx[], const int yy[], const int points, const bool open=false) + {return new GMapPoly(xx,yy,points,open);} + + /// Virtual destructor. + virtual ~GMapPoly(); + + /// Returns 1 if side #side# crosses the specified rectangle #rect#. + bool does_side_cross_rect(const GRect & grect, int side); + + /// Returns the number of vertices in the polygon + int get_points_num(void) const; + + /// Returns the number sides in the polygon + int get_sides_num(void) const; + + /// Returns x coordinate of vertex number #i# + int get_x(int i) const; + + /// Returns y coordinate of vertex number #i# + int get_y(int i) const; + + /// Moves vertex #i# to position (#x#, #y#) + void move_vertex(int i, int x, int y); + + /// Adds a new vertex and returns number of vertices in the polygon + int add_vertex(int x, int y); + + /// Closes the polygon if it is not closed + void close_poly(); + /// Optimizes the polygon + void optimize_data(void); + /// Checks validity of the polygon + char const * const check_data(void); + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapPoly + virtual MapAreaType const get_shape_type( void ) const { return POLY; }; + /// Returns #"poly"# all the time + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the polygon + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function generating a list of defining coordinates + void get_coords( GList<int> & CoordList ) const; + /// Virtual function maps polygon from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps polygon from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +private: + bool open; + int points, sides; + GTArray<int> xx, yy; + static int sign(int x); + static bool is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2); + static bool do_segments_intersect(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22); + static bool are_segments_parallel(int x11, int y11, int x12, int y12, + int x21, int y21, int x22, int y22); +}; + +// ---------- GMAPOVAL --------- + +/** Implements elliptical map areas. The only supported types of border + are #NO_BORDER#, #XOR_BORDER# and #SOLID_BORDER#. Its contents can not + be highlighted either. */ + +class GMapOval: public GMapArea +{ +protected: + GMapOval(void); + GMapOval(const GRect & rect); +public: + /// Default creator. + static GP<GMapOval> create(void) {return new GMapOval();} + + /// Create from the specified GRect. + static GP<GMapOval> create(const GRect &rect) {return new GMapOval(rect);} + + /// Virtual destructor. + virtual ~GMapOval(); + + /// Returns (xmax-xmin)/2 + int get_a(void) const; + /// Returns (ymax-ymin)/2 + int get_b(void) const; + /// Returns the lesser of \Ref{get_a}() and \Ref{get_b}() + int get_rmin(void) const; + /// Returns the greater of \Ref{get_a}() and \Ref{get_b}() + int get_rmax(void) const; + + virtual GUTF8String get_xmltag(const int height) const; + /// Returns MapOval + virtual MapAreaType const get_shape_type( void ) const { return OVAL; }; + /// Returns #"oval"# + virtual char const * const get_shape_name(void) const; + /// Returns a copy of the oval + virtual GP<GMapArea> get_copy(void) const; + /// Virtual function maps oval from one area to another using mapper + virtual void map(GRectMapper &mapper); + /// Virtual function unmaps oval from one area to another using mapper + virtual void unmap(GRectMapper &mapper); +protected: + virtual int gma_get_xmin(void) const; + virtual int gma_get_ymin(void) const; + virtual int gma_get_xmax(void) const; + virtual int gma_get_ymax(void) const; + virtual void gma_move(int dx, int dy); + virtual void gma_resize(int new_width, int new_height); + virtual void gma_transform(const GRect & grect); + virtual bool gma_is_point_inside(const int x, const int y) const; + virtual char const * const gma_check_object(void) const; + virtual GUTF8String gma_print(void); +private: + int rmax, rmin; + int a, b; + int xf1, yf1, xf2, yf2; + int xmin, ymin, xmax, ymax; + + void initialize(void); +}; + +inline +GMapRect::operator GRect(void) +{ + return GRect(xmin, ymin, xmax-xmin, ymax-ymin); +} + +inline int +GMapRect::gma_get_xmin(void) const { return xmin; } + +inline int +GMapRect::gma_get_ymin(void) const { return ymin; } + +inline int +GMapRect::gma_get_xmax(void) const { return xmax; } + +inline int +GMapRect::gma_get_ymax(void) const { return ymax; } + +inline char const * const +GMapRect::gma_check_object(void) const{ return ""; } + +inline char const * const +GMapRect::get_shape_name(void) const { return RECT_TAG; } + +inline int +GMapPoly::get_points_num(void) const { return points; } + +inline int +GMapPoly::get_sides_num(void) const { return sides; } + +inline int +GMapPoly::get_x(int i) const { return xx[i]; } + +inline int +GMapPoly::get_y(int i) const { return yy[i]; } + +inline char const * const +GMapPoly::get_shape_name(void) const { return POLY_TAG; } + +inline int +GMapOval::get_a(void) const { return a; } + +inline int +GMapOval::get_b(void) const { return b; } + +inline int +GMapOval::get_rmin(void) const { return rmin; } + +inline int +GMapOval::get_rmax(void) const { return rmax; } + +inline int +GMapOval::gma_get_xmin(void) const { return xmin; } + +inline int +GMapOval::gma_get_ymin(void) const { return ymin; } + +inline int +GMapOval::gma_get_xmax(void) const { return xmax; } + +inline int +GMapOval::gma_get_ymax(void) const { return ymax; } + +inline char const * const +GMapOval::get_shape_name(void) const { return OVAL_TAG; } + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GOS.cpp b/kviewshell/plugins/djvu/libdjvu/GOS.cpp new file mode 100644 index 00000000..35aa8997 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GOS.cpp @@ -0,0 +1,370 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GOS.cpp,v 1.12 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GException.h" +#include "GThreads.h" +#include "GOS.h" +#include "GURL.h" + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +#ifdef WIN32 +# include <atlbase.h> +# include <windows.h> +# include <direct.h> +#endif + +#ifdef OS2 +# define INCL_DOS +# include <os2.h> +#endif + +#if defined(UNIX) || defined(OS2) +# include <errno.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/time.h> +# include <fcntl.h> +# include <pwd.h> +# include <stdio.h> +# include <unistd.h> +#endif + +#ifdef macintosh +# include <unix.h> +# include <errno.h> +# include <unistd.h> +#endif + +// -- TRUE FALSE +#undef TRUE +#undef FALSE +#define TRUE 1 +#define FALSE 0 + +// -- MAXPATHLEN +#ifndef MAXPATHLEN +# ifdef _MAX_PATH +# define MAXPATHLEN _MAX_PATH +# else +# define MAXPATHLEN 1024 +# endif +#else +# if ( MAXPATHLEN < 1024 ) +# undef MAXPATHLEN +# define MAXPATHLEN 1024 +# endif +#endif + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#if defined(AUTOCONF) && !defined(HAVE_STRERROR) +# define NEED_STRERROR +#elif defined(sun) && !defined(__svr4__) +# define NEED_STRERROR +#elif defined(REIMPLEMENT_STRERROR) +# define NEED_STRERROR +#endif +#ifdef NEED_STRERROR +char * +strerror(int errno) +{ + extern int sys_nerr; + extern char *sys_errlist[]; + if (errno>0 && errno<sys_nerr) + return sys_errlist[errno]; + return "unknown stdio error"; +} +#endif + + +static const char slash='/'; +static const char percent='%'; +static const char backslash='\\'; +static const char colon=':'; +static const char dot='.'; +static const char nillchar=0; + + +// ----------------------------------------- +// Functions for dealing with filenames +// ----------------------------------------- + +static inline int +finddirsep(const GUTF8String &fname) +{ +#if defined(UNIX) + return fname.rsearch('/',0); +#elif defined(WIN32) || defined(OS2) + return fname.rcontains("\\/",0); +#elif defined(macintosh) + return fname.rcontains(":/",0); +#else +#error "Define something here for your operating system" +#endif +} + + +// basename(filename[, suffix]) +// -- returns the last component of filename and removes suffix +// when present. works like /bin/basename. +GUTF8String +GOS::basename(const GUTF8String &gfname, const char *suffix) +{ + if(!gfname.length()) + return gfname; + + const char *fname=gfname; +#if defined(WIN32) || defined(OS2) + // Special cases + if (fname[1] == colon) + { + if(!fname[2]) + { + return gfname; + } + if (!fname[3] && (fname[2]== slash || fname[2]== backslash)) + { + char string_buffer[4]; + string_buffer[0] = fname[0]; + string_buffer[1] = colon; + string_buffer[2] = backslash; + string_buffer[3] = 0; + return string_buffer; + } + } +#endif + + + // Allocate buffer + GUTF8String retval(gfname,finddirsep(gfname)+1,(unsigned int)(-1)); + fname=retval; + + // Process suffix + if (suffix) + { + if (suffix[0]== dot ) + suffix ++; + if (suffix[0]) + { + const GUTF8String gsuffix(suffix); + const int sl = gsuffix.length(); + const char *s = fname + strlen(fname); + if (s > fname + sl) + { + s = s - (sl + 1); + if(*s == dot && (GUTF8String(s+1).downcase() == gsuffix.downcase())) + { + retval.setat((int)((size_t)s-(size_t)fname),0); + } + } + } + } + return retval; +} + + + +// errmsg -- +// -- A small helper function returning a +// stdio error message in a static buffer. + +static GNativeString +errmsg() +{ + GNativeString buffer; + const char *errname = strerror(errno); + buffer.format("%s (errno = %d)", errname, errno); + return buffer; +} + + + +// ----------------------------------------- +// Functions for measuring time +// ----------------------------------------- + +// ticks() -- +// -- returns the number of milliseconds elapsed since +// a system dependent date. +unsigned long +GOS::ticks() +{ +#if defined(UNIX) + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) + G_THROW(errmsg()); + return (unsigned long)( ((tv.tv_sec & 0xfffff)*1000) + + (tv.tv_usec/1000) ); +#elif defined(WIN32) + DWORD clk = GetTickCount(); + return (unsigned long)clk; +#elif defined(OS2) + ULONG clk = 0; + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, (PVOID)&clk, sizeof(ULONG)); + return clk; +#elif defined(macintosh) + return (unsigned long)((double)TickCount()*16.66); +#else +# error "Define something here for your operating system" +#endif +} + +// sleep(int milliseconds) -- +// -- sleeps during the specified time (in milliseconds) +void +GOS::sleep(int milliseconds) +{ +#if defined(UNIX) + struct timeval tv; + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds - (tv.tv_sec * 1000)) * 1000; +# if defined(THREADMODEL) && (THREADMODEL==COTHREADS) + GThread::select(0, NULL, NULL, NULL, &tv); +# else + select(0, NULL, NULL, NULL, &tv); +# endif +#elif defined(WIN32) + Sleep(milliseconds); +#elif defined(OS2) + DosSleep(milliseconds); +#elif defined(macintosh) + unsigned long tick = ticks(), now; + while (1) { + now = ticks(); + if ((tick+milliseconds) < now) + break; + GThread::yield(); + } +#endif +} + + +// ----------------------------------------- +// Testing +// ----------------------------------------- + +// cwd([dirname]) +// -- changes directory to dirname (when specified). +// returns the full path name of the current directory. +GUTF8String +GOS::cwd(const GUTF8String &dirname) +{ +#if defined(UNIX) || defined(macintosh) || defined(OS2) + if (dirname.length() && chdir(dirname.getUTF82Native())==-1)//MBCS cvt + G_THROW(errmsg()); + char *string_buffer; + GPBuffer<char> gstring_buffer(string_buffer,MAXPATHLEN+1); + char *result = getcwd(string_buffer,MAXPATHLEN); + if (!result) + G_THROW(errmsg()); + return GNativeString(result).getNative2UTF8();//MBCS cvt +#elif defined (WIN32) + char drv[2]; + if (dirname.length() && _chdir(dirname.getUTF82Native())==-1)//MBCS cvt + G_THROW(errmsg()); + drv[0]= dot ; drv[1]=0; + char *string_buffer; + GPBuffer<char> gstring_buffer(string_buffer,MAXPATHLEN+1); + char *result = getcwd(string_buffer,MAXPATHLEN); + GetFullPathName(drv, MAXPATHLEN, string_buffer, &result); + return GNativeString(string_buffer).getNative2UTF8();//MBCS cvt +#else +#error "Define something here for your operating system" +#endif +} + +GUTF8String +GOS::getenv(const GUTF8String &name) +{ + GUTF8String retval; + if(name.length()) + { + const char *env=::getenv(name.getUTF82Native()); + if(env) + { + retval=GNativeString(env); + } + } + return retval; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GOS.h b/kviewshell/plugins/djvu/libdjvu/GOS.h new file mode 100644 index 00000000..3e57fb40 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GOS.h @@ -0,0 +1,161 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GOS.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GOS_H_ +#define _GOS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GOS.h + Files #"GOS.h"# and #"GOS.cpp"# implement operating system + dependent functions with a unified interface. All these functions + are implemented as static member of class \Ref{GOS}. + Functions are provided for testing the presence of a file or a directory + (\Ref{GOS::is_file}, \Ref{GOS::is_dir}), for manipulating file and directory names + (\Ref{GOS::dirname}, \Ref{GOS::basename}, \Ref{GOS::expand_name}, + for obtaining and changing the current directory (\Ref{GOS::cwd}), + for converting between file names and urls (\Ref{GOS::filename_to_url}, + \Ref{GOS::url_to_filename}), and for counting time (\Ref{GOS::ticks}, + \Ref{GOS::sleep}). + + @memo + Operating System dependent functions. + @author + L\'eon Bottou <[email protected]> -- Initial implementation + @version + #$Id: GOS.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# +*/ +//@{ + +#include "DjVuGlobal.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GURL; + +/** Operating System dependent functions. */ +class GOS +{ + public: + // ----------------------------------------- + // Functions for dealing with filenames + // ----------------------------------------- + + /** Returns the last component of file name #filename#. If the filename + suffix matches argument #suffix#, the filename suffix is removed. This + function works like the unix command #/bin/basename#, but also supports + the naming conventions of other operating systems. */ + static GUTF8String basename(const GUTF8String &filename, const char *suffix=0); + + /** Sets and returns the current working directory. + When argument #dirname# is specified, the current directory is changed + to #dirname#. This function always returns the fully qualified name + of the current directory. */ + static GUTF8String cwd(const GUTF8String &dirname=GUTF8String()); + + // ----------------------------------------- + // Functions for measuring time + // ----------------------------------------- + + /** Returns a number of elapsed milliseconds. This number counts elapsed + milliseconds since a operating system dependent date. This function is + useful for timing code. */ + static unsigned long ticks(); + + /** Sleeps during the specified time expressed in milliseconds. + Other threads can run while the calling thread sleeps. */ + static void sleep(int milliseconds); + + /// Read the named variable from the environment, and converts it to UTF8. + static GUTF8String getenv(const GUTF8String &name); + +#if 0 + // ------------------------------------------- + // Functions for converting filenames and urls + // ------------------------------------------- + /** Encodes all reserved characters, so that the #filename# can be + used inside a URL. Every reserved character is encoded using escape + sequence in the form of #%XX#. The legal characters are alphanumeric and + #$-_.+!*'(),:#. + Use \Ref{decode_reserved}() to convert the URL back to the filename. */ +// static GString encode_reserved(const char * filename); + static GString encode_mbcs_reserved(const char * filename);/*MBCS*/ +#endif + +}; + + +//@} +// ------------ + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp new file mode 100644 index 00000000..81c1dd71 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp @@ -0,0 +1,1676 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GPixmap.cpp,v 1.12 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implements class PIXMAP +// Author: Leon Bottou 07/1997 + + + +#include "GPixmap.h" + +#include "GString.h" +#include "GException.h" +#include "ByteStream.h" +#include "GRect.h" +#include "GBitmap.h" +#include "GThreads.h" +#include "Arrays.h" +#include "JPEGDecoder.h" +#include <stdlib.h> +#include <math.h> +#include <assert.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +////////////////////////////////////////////////// +// ------- predefined colors +////////////////////////////////////////////////// + + +const GPixel GPixel::WHITE = { 255, 255, 255 }; +const GPixel GPixel::BLACK = { 0, 0, 0 }; +const GPixel GPixel::BLUE = { 255, 0, 0 }; +const GPixel GPixel::GREEN = { 0, 255, 0 }; +const GPixel GPixel::RED = { 0, 0, 255 }; + + +////////////////////////////////////////////////// +// ----- utilities +////////////////////////////////////////////////// + + +static const GPixel * +new_gray_ramp(int grays,GPixel *ramp) +{ + int color = 0xff0000; + int decrement = color / (grays-1); + for (int i=0; i<grays; i++) + { + int level = color >> 16; + ramp[i].b = level; + ramp[i].g = level; + ramp[i].r = level; + color -= decrement; + } + return ramp; +} + + +static inline int +mini(int x, int y) +{ + return (x < y ? x : y); +} + + +static inline int +maxi(int x, int y) +{ + return (x > y ? x : y); +} + + +static inline void +euclidian_ratio(int a, int b, int &q, int &r) +{ + q = a / b; + r = a - b*q; + if (r < 0) + { + q -= 1; + r += b; + } +} + + +////////////////////////////////////////////////// +// global lock used by some rare operations +////////////////////////////////////////////////// + +static GMonitor &pixmap_monitor() { + static GMonitor xpixmap_monitor; + return xpixmap_monitor; +} + + +////////////////////////////////////////////////// +// constructors and destructors +////////////////////////////////////////////////// + + +GPixmap::~GPixmap() +{ + delete [] pixels_data; +} + +void +GPixmap::destroy(void) +{ + delete [] pixels_data; + pixels = pixels_data = 0; +} + +GPixmap::GPixmap() +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ +} + +GPixmap::GPixmap(int nrows, int ncolumns, const GPixel *filler) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(nrows, ncolumns, filler); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(ByteStream &bs) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(bs); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GBitmap &ref) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, 0); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GBitmap &ref, const GRect &rect) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, rect, 0); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GPixmap &ref) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + +GPixmap::GPixmap(const GPixmap &ref, const GRect &rect) +: nrows(0), ncolumns(0), pixels(0), pixels_data(0) +{ + G_TRY + { + init(ref, rect); + } + G_CATCH_ALL + { + destroy(); + G_RETHROW; + } + G_ENDCATCH; +} + + + +////////////////////////////////////////////////// +// Initialization +////////////////////////////////////////////////// + + +void +GPixmap::init(int arows, int acolumns, const GPixel *filler) +{ + destroy(); + nrows = arows; + ncolumns = acolumns; + nrowsize = acolumns; + int npix = nrows * nrowsize; + if (npix > 0) + { + pixels = pixels_data = new GPixel[npix]; + if (filler) + { + while (--npix>=0) + pixels_data[npix] = *filler; + } + } +} + + +void +GPixmap::init(const GBitmap &ref, const GPixel *userramp) +{ + init(ref.rows(), ref.columns(), 0); + GPixel *xramp; + GPBuffer<GPixel> gxramp(xramp); + if (nrows>0 && ncolumns>0) + { + // Create pixel ramp + const GPixel *ramp = userramp; + if (!userramp) + { + gxramp.resize(256); + gxramp.clear(); + ramp = new_gray_ramp(ref.get_grays(),xramp); + } + // Copy pixels + for (int y=0; y<nrows; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = ramp[ src[x] ]; + } + // Free ramp +// if (!userramp) +// delete [] (GPixel*)ramp; + } +} + + +void +GPixmap::init(const GBitmap &ref, const GRect &rect, const GPixel *userramp) +{ + init(rect.height(), rect.width(), 0); + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + GPixel *xramp; + GPBuffer<GPixel> gxramp(xramp); + // allocate ramp + const GPixel *ramp = userramp; + if (!userramp) + { + gxramp.resize(256); + gxramp.clear(); + ramp = new_gray_ramp(ref.get_grays(),xramp); + } + // copy pixels + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = ramp[ src[x] ]; + } + // free ramp +// if (!userramp) +// delete [] (GPixel*) ramp; + } +} + + +void +GPixmap::init(const GPixmap &ref) +{ + init(ref.rows(), ref.columns(), 0); + if (nrows>0 && ncolumns>0) + { + for (int y=0; y<nrows; y++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::init(const GPixmap &ref, const GRect &rect) +{ + init(rect.height(), rect.width(), 0); + // compute destination rectangle + GRect rect2(0, 0, ref.columns(), ref.rows() ); + rect2.intersect(rect2, rect); + rect2.translate(-rect.xmin, -rect.ymin); + // copy bits + if (! rect2.isempty()) + { + for (int y=rect2.ymin; y<rect2.ymax; y++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::donate_data(GPixel *data, int w, int h) +{ + destroy(); + nrows = h; + ncolumns = w; + nrowsize = w; + pixels_data=pixels=data; +} + + +GPixel * +GPixmap::take_data(size_t &offset) +{ + GPixel *ret = pixels_data; + pixels_data = 0; + offset = 0; + return ret; +} + + + +////////////////////////////////////////////////// +// Save and load ppm files +////////////////////////////////////////////////// + + +static unsigned int +read_integer(char &c, ByteStream &bs) +{ + unsigned int x = 0; + // eat blank before integer + while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#') + { + if (c=='#') + do { } while (bs.read(&c,1) && c!='\n' && c!='\r'); + c = 0; + bs.read(&c, 1); + } + // check integer + if (c<'0' || c>'9') + G_THROW( ERR_MSG("GPixmap.no_int") ); + // eat integer + while (c>='0' && c<='9') + { + x = x*10 + c - '0'; + c = 0; + bs.read(&c, 1); + } + return x; +} + + +void +GPixmap::init(ByteStream &bs) +{ + // Read header + int raw = 0; + char magic[2]; + magic[0] = magic[1] = 0; + bs.readall((void*)magic, sizeof(magic)); + if (magic[0]=='P' && magic[1]=='3') + { + raw = 0; + }else if (magic[0]=='P' && magic[1]=='6') + { + raw = 1; + }else + { +#ifdef NEED_JPEG_DECODER + bs.seek(0L); + JPEGDecoder::decode(bs,*this); + return; +#else // NEED_JPEG_DECODER + + G_THROW( ERR_MSG("GPixmap.unk_PPM") ); +#endif // NEED_JPEG_DECODER + } + // Read image size + char lookahead = '\n'; + int acolumns = read_integer(lookahead, bs); + int arows = read_integer(lookahead, bs); + int maxval = read_integer(lookahead, bs); + if (maxval > 255) + G_THROW("Cannot read PPM with depth greater than 24 bits."); + init(arows, acolumns, 0); + // Read image data + if (raw) + { + GTArray<unsigned char> line(ncolumns*3); + for (int y=nrows-1; y>=0; y--) + { + GPixel *p = (*this)[y]; + unsigned char *rgb = &line[0]; + if ( bs.readall((void*)rgb, ncolumns*3) < (size_t)(ncolumns*3)) + G_THROW( ByteStream::EndOfFile ); + for (int x=0; x<ncolumns; x+=1, rgb+=3) + { + p[x].r = rgb[0]; + p[x].g = rgb[1]; + p[x].b = rgb[2]; + } + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = read_integer(lookahead, bs); + p[x].g = read_integer(lookahead, bs); + p[x].b = read_integer(lookahead, bs); + } + } + } + // Process small values of maxval + if (maxval>0 && maxval<255) + { + char table[256]; + for (int i=0; i<256; i++) + table[i] = (i<maxval ? (255*i + maxval/2) / maxval : 255); + for (int y=0; y<nrows; y++) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = table[p[x].r]; + p[x].g = table[p[x].g]; + p[x].b = table[p[x].b]; + } + } + } +} + + +void +GPixmap::save_ppm(ByteStream &bs, int raw) const +{ + GUTF8String head; + head.format("P%c\n%d %d\n255\n", (raw ? '6' : '3'), ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + if (raw) + { + int rowsize = ncolumns+ncolumns+ncolumns; + GTArray<unsigned char> xrgb(rowsize); + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char *d = xrgb; + for (int x=0; x<ncolumns; x++) + { + *d++ = p[x].r; + *d++ = p[x].g; + *d++ = p[x].b; + } + bs.writall((void*)(unsigned char*)xrgb, ncolumns * 3); + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char eol='\n'; + for (int x=0; x<ncolumns; ) + { + head.format("%d %d %d ", p[x].r, p[x].g, p[x].b); + bs.writall((void*)(const char *)head, head.length()); + x += 1; + if (x==ncolumns || (x&0x7)==0) + bs.write((void*)&eol, 1); + } + } + } +} + + + + +////////////////////////////////////////////////// +// Color correction +////////////////////////////////////////////////// + + +static void +color_correction_table(double gamma, unsigned char gtable[256] ) +{ + // Check argument + if (gamma<0.1 || gamma>10.0) + G_THROW( ERR_MSG("GPixmap.bad_param") ); + if (gamma<1.001 && gamma>0.999) + { + // Trivial correction + for (int i=0; i<256; i++) + gtable[i] = i; + } + else + { + // Must compute the correction + for (int i=0; i<256; i++) + { + double x = (double)(i)/255.0; +#ifdef BEZIERGAMMA + double t = ( sqrt(1.0+(gamma*gamma-1.0)*x) - 1.0 ) / (gamma - 1.0); + x = ( (1.0 - gamma)*t + 2.0 * gamma ) * t / (gamma + 1.0); +#else + x = pow(x, 1.0/gamma); +#endif + gtable[i] = (int) floor(255.0 * x + 0.5); + } + // Make sure that min and max values are exactly black or white + gtable[0] = 0; + gtable[255] = 255; + } +} + +static void +color_correction_table_cache(double gamma, unsigned char gtable[256] ) +{ + // Compute color correction table + if (gamma<1.001 && gamma>0.999) + { + color_correction_table(gamma, gtable); + } + else + { + static double lgamma = -1.0; + static unsigned char ctable[256]; + GMonitorLock lock(&pixmap_monitor()); + if (gamma != lgamma) + { + color_correction_table(gamma, ctable); + lgamma = gamma; + } + memcpy(gtable, ctable, 256*sizeof(unsigned char)); + } +} + +void +GPixmap::color_correct(double gamma_correction) +{ + // Trivial corrections + if (gamma_correction>0.999 && gamma_correction<1.001) + return; + // Compute correction table + unsigned char gtable[256]; + color_correction_table_cache(gamma_correction, gtable); + // Perform correction + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = gtable[ pix->r ]; + pix->g = gtable[ pix->g ]; + pix->b = gtable[ pix->b ]; + } + } +} + + +void +GPixmap::color_correct(double gamma_correction, GPixel *pix, int npixels) +{ + // Trivial corrections + if (gamma_correction>0.999 && gamma_correction<1.001) + return; + // Compute correction table + unsigned char gtable[256]; + color_correction_table_cache(gamma_correction, gtable); + // Perform correction + while (--npixels>=0) + { + pix->r = gtable[pix->r]; + pix->g = gtable[pix->g]; + pix->b = gtable[pix->b]; + pix++; + } +} + + + +////////////////////////////////////////////////// +// Dithering +////////////////////////////////////////////////// + + +void +GPixmap::ordered_666_dither(int xmin, int ymin) +{ + static unsigned char quantize[256+0x33+0x33]; + static unsigned char *quant = quantize + 0x33; + static char dither_ok = 0; + static short dither[16][16] = + { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + }; + // Prepare tables + if (!dither_ok) + { + int i, j; + for (i=0; i<16; i++) + for (j=0; j<16; j++) + dither[i][j] = ((255 - 2*dither[i][j]) * 0x33) / 512; + j = -0x33; + for (i=0x19; i<256; i+=0x33) + while (j <= i) + quant[j++] = i-0x19; + assert(i-0x19 == 0xff); + while (j< 256+0x33) + quant[j++] = i-0x19; + dither_ok = 1; + } + // Go dithering + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; + pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; + pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; + } + } +} + +void +GPixmap::ordered_32k_dither(int xmin, int ymin) +{ + static unsigned char quantize[256+8+8]; + static unsigned char *quant = quantize + 8; + static char dither_ok = 0; + static short dither[16][16] = + { + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } + }; + // Prepare tables + if (!dither_ok) + { + int i, j; + for (i=0; i<16; i++) + for (j=0; j<16; j++) + dither[i][j] = ((255 - 2*dither[i][j]) * 8) / 512; + j = -8; + for (i=3; i<256; i+=8) + while (j <= i) + quant[j++] = i; + while (j<256+8) + quant[j++] = 0xff; + dither_ok = 1; + } + // Go dithering + for (int y=0; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; + pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; + pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; + } + } +} + + +////////////////////////////////////////////////// +// Upsample Downsample +////////////////////////////////////////////////// + + +void +GPixmap::downsample(const GPixmap *src, int factor, const GRect *pdr) +{ + // check arguments + GRect rect(0, 0, (src->columns()+factor-1)/factor, (src->rows()+factor-1)/factor); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow1") ); + rect = *pdr; + } + + // precompute inverse map + static int invmap[256]; + static int invmapok = 0; + if (! invmapok) + { + invmapok = 1; + for (int i=1; i<(int)(sizeof(invmap)/sizeof(int)); i++) + invmap[i] = 0x10000 / i; + } + + // initialise pixmap + init(rect.height(), rect.width(), 0); + + // determine starting and ending points in source rectangle + int sy = rect.ymin * factor; + int sxz = rect.xmin * factor; + + + // loop over source rows + const GPixel *sptr = (*src)[sy]; + GPixel *dptr = (*this)[0]; + for (int y=0; y<nrows; y++) + { + int sx = sxz; + // loop over source columns + for (int x=0; x<ncolumns; x++) + { + int r=0, g=0, b=0, s=0; + // compute average bounds + const GPixel *ksptr = sptr; + int lsy = sy + factor; + if (lsy > (int)src->rows()) + lsy = (int)src->rows(); + int lsx = sx + factor; + if (lsx > (int)src->columns()) + lsx = (int)src->columns(); + // compute average + for (int rsy=sy; rsy<lsy; rsy++) + { + for (int rsx = sx; rsx<lsx; rsx++) + { + r += ksptr[rsx].r; + g += ksptr[rsx].g; + b += ksptr[rsx].b; + s += 1; + } + ksptr += src->rowsize(); + } + // set pixel color + if (s >= (int)(sizeof(invmap)/sizeof(int))) + { + dptr[x].r = r / s; + dptr[x].g = g / s; + dptr[x].b = b / s; + } + else + { + dptr[x].r = (r*invmap[s] + 0x8000) >> 16; + dptr[x].g = (g*invmap[s] + 0x8000) >> 16; + dptr[x].b = (b*invmap[s] + 0x8000) >> 16; + } + // next column + sx = sx + factor; + } + // next row + sy = sy + factor; + sptr = sptr + factor * src->rowsize(); + dptr = dptr + rowsize(); + } +} + +void +GPixmap::upsample(const GPixmap *src, int factor, const GRect *pdr) +{ + // check arguments + GRect rect(0, 0, src->columns()*factor, src->rows()*factor); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow2") ); + rect = *pdr; + } + // initialise pixmap + init(rect.height(), rect.width(), 0); + // compute starting point in source rectangle + int sy, sy1, sxz, sx1z; + euclidian_ratio(rect.ymin, factor, sy, sy1); + euclidian_ratio(rect.xmin, factor, sxz, sx1z); + // loop over rows + const GPixel *sptr = (*src)[sy]; + GPixel *dptr = (*this)[0]; + for (int y=0; y<nrows; y++) + { + // loop over columns + int sx = sxz; + int sx1 = sx1z; + for (int x=0; x<ncolumns; x++) + { + dptr[x] = sptr[sx]; + // next column + if (++sx1 >= factor) + { + sx1 = 0; + sx += 1; + } + } + // next row + dptr += rowsize(); + if (++sy1 >= factor) + { + sy1 = 0; + sptr += src->rowsize(); + } + } +} + + +static inline void +downsample_4x4_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) +{ + const GPixel *x = s; + const GPixel *y = x + sadd; + d[0].b = ( 11*x[0].b + 2*(x[1].b + y[0].b ) + y[1].b + 8) >> 4; + d[0].g = ( 11*x[0].g + 2*(x[1].g + y[0].g ) + y[1].g + 8) >> 4; + d[0].r = ( 11*x[0].r + 2*(x[1].r + y[0].r ) + y[1].r + 8) >> 4; + d[1].b = ( 7*(x[1].b + x[2].b) + y[1].b + y[2].b + 8 ) >> 4; + d[1].g = ( 7*(x[1].g + x[2].g) + y[1].g + y[2].g + 8 ) >> 4; + d[1].r = ( 7*(x[1].r + x[2].r) + y[1].r + y[2].r + 8 ) >> 4; + d[2].b = ( 11*x[3].b + 2*(x[2].b + y[3].b ) + y[2].b + 8) >> 4; + d[2].g = ( 11*x[3].g + 2*(x[2].g + y[3].g ) + y[2].g + 8) >> 4; + d[2].r = ( 11*x[3].r + 2*(x[2].r + y[3].r ) + y[2].r + 8) >> 4; + d = d + dadd; + x = x + sadd + sadd; + d[0].b = ( 7*(x[0].b + y[0].b) + x[1].b + y[1].b + 8 ) >> 4; + d[0].g = ( 7*(x[0].g + y[0].g) + x[1].g + y[1].g + 8 ) >> 4; + d[0].r = ( 7*(x[0].r + y[0].r) + x[1].r + y[1].r + 8 ) >> 4; + d[1].b = ( x[2].b + y[2].b + x[1].b + y[1].b + 2 ) >> 2; + d[1].g = ( x[2].g + y[2].g + x[1].g + y[1].g + 2 ) >> 2; + d[1].r = ( x[2].r + y[2].r + x[1].r + y[1].r + 2 ) >> 2; + d[2].b = ( 7*(x[3].b + y[3].b) + x[2].b + y[2].b + 8 ) >> 4; + d[2].g = ( 7*(x[3].g + y[3].g) + x[2].g + y[2].g + 8 ) >> 4; + d[2].r = ( 7*(x[3].r + y[3].r) + x[2].r + y[2].r + 8 ) >> 4; + d = d + dadd; + y = y + sadd + sadd; + d[0].b = ( 11*y[0].b + 2*(y[1].b + x[0].b ) + x[1].b + 8) >> 4; + d[0].g = ( 11*y[0].g + 2*(y[1].g + x[0].g ) + x[1].g + 8) >> 4; + d[0].r = ( 11*y[0].r + 2*(y[1].r + x[0].r ) + x[1].r + 8) >> 4; + d[1].b = ( 7*(y[1].b + y[2].b) + x[1].b + x[2].b + 8 ) >> 4; + d[1].g = ( 7*(y[1].g + y[2].g) + x[1].g + x[2].g + 8 ) >> 4; + d[1].r = ( 7*(y[1].r + y[2].r) + x[1].r + x[2].r + 8 ) >> 4; + d[2].b = ( 11*y[3].b + 2*(y[2].b + x[3].b ) + x[2].b + 8) >> 4; + d[2].g = ( 11*y[3].g + 2*(y[2].g + x[3].g ) + x[2].g + 8) >> 4; + d[2].r = ( 11*y[3].r + 2*(y[2].r + x[3].r ) + x[2].r + 8) >> 4; +} + + +static inline void +upsample_2x2_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) +{ + const GPixel *x = s; + const GPixel *y = x + sadd; + d[0] = x[0]; + d[1].b = (x[0].b + x[1].b + 1) >> 1; + d[1].g = (x[0].g + x[1].g + 1) >> 1; + d[1].r = (x[0].r + x[1].r + 1) >> 1; + d[2] = x[1]; + d = d + dadd; + d[0].b = (x[0].b + y[0].b + 1) >> 1; + d[0].g = (x[0].g + y[0].g + 1) >> 1; + d[0].r = (x[0].r + y[0].r + 1) >> 1; + d[1].b = (x[0].b + y[0].b + x[1].b + y[1].b + 2) >> 2; + d[1].g = (x[0].g + y[0].g + x[1].g + y[1].g + 2) >> 2; + d[1].r = (x[0].r + y[0].r + x[1].r + y[1].r + 2) >> 2; + d[2].b = (x[1].b + y[1].b + 1) >> 1; + d[2].g = (x[1].g + y[1].g + 1) >> 1; + d[2].r = (x[1].r + y[1].r + 1) >> 1; + d = d + dadd; + d[0] = y[0]; + d[1].b = (y[0].b + y[1].b + 1) >> 1; + d[1].g = (y[0].g + y[1].g + 1) >> 1; + d[1].r = (y[0].r + y[1].r + 1) >> 1; + d[2] = y[1]; +} + + +static inline void +copy_to_partial(int w, int h, + const GPixel *s, int sadd, + GPixel *d, int dadd, int xmin, int xmax, int ymin, int ymax) +{ + int y = 0; + while (y<ymin && y<h) + { + y += 1; + s += sadd; + d += dadd; + } + while (y<ymax && y<h) + { + int x = (xmin>0 ? xmin : 0); + while (x<w && x<xmax) + { + d[x] = s[x]; + x++; + } + y += 1; + s += sadd; + d += dadd; + } +} + + +static inline void +copy_line(const GPixel *s, int smin, int smax, + GPixel *d, int dmin, int dmax) +{ + int x = dmin; + while (x < smin) + { + d[x] = s[smin]; + x++; + } + while (x < dmax && x < smax) + { + d[x] = s[x]; + x++; + } + while (x < dmax) + { + d[x] = s[smax]; + x++; + } +} + + +static inline void +copy_from_partial(int w, int h, + const GPixel *s, int sadd, int xmin, int xmax, int ymin, int ymax, + GPixel *d, int dadd) +{ + int y = 0; + s += (ymin>0 ? sadd * ymin : 0); + while (y<ymin && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } + while (y<ymax && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + s += sadd; + d += dadd; + } + s -= sadd; + while (y < h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } +} + + + + + +void +GPixmap::downsample43(const GPixmap *src, const GRect *pdr) +{ + // check arguments + int srcwidth = src->columns(); + int srcheight = src->rows(); + int destwidth = (srcwidth * 3 + 3 ) / 4; + int destheight = (srcheight * 3 + 3) / 4; + GRect rect(0, 0, destwidth, destheight); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow3") ); + rect = *pdr; + destwidth = rect.width(); + destheight = rect.height(); + } + // initialize pixmap + init(destheight, destwidth, 0); + + // compute bounds + int dxz, dy; // location of bottomleft block in destination image + int sxz, sy; // location of bottomleft block in source image + euclidian_ratio(rect.ymin, 3, sy, dy); + euclidian_ratio(rect.xmin, 3, sxz, dxz); + sxz = 4 * sxz; + sy = 4 * sy; + dxz = - dxz; + dy = - dy; + + // prepare variables + int sadd = src->rowsize(); + int dadd = this->rowsize(); + const GPixel *sptr = (*src)[0] + sy * sadd; + GPixel *dptr = (*this)[0] + dy * dadd; + int s4add = 4 * sadd; + int d3add = 3 * dadd; + + // iterate over row blocks + while (dy < destheight) + { + int sx = sxz; + int dx = dxz; + // iterate over column blocks + while (dx < destwidth) + { + GPixel xin[16], xout[9]; + + if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) + { + if (sx+4<=srcwidth && sy+4<=srcheight) + { + downsample_4x4_to_3x3(sptr+sx, sadd, dptr+dx, dadd); + } + else + { + copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); + downsample_4x4_to_3x3(xin, 4, dptr+dx, dadd); + } + } + else + { + if (sx+4<=srcwidth && sy+4<=srcheight) + { + downsample_4x4_to_3x3(sptr+sx, sadd, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd,-dx,destwidth-dx,-dy,destheight-dy); + } + else + { + copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); + downsample_4x4_to_3x3(xin, 4, xout, 3); + copy_to_partial(3,3, xout,3, dptr+dx,dadd,-dx,destwidth-dx,-dy,destheight-dy); + } + } + // next column + dx += 3; + sx += 4; + } + // next row + dy += 3; + dptr += d3add; + sy += 4; + sptr += s4add; + } +} + + +void +GPixmap::upsample23(const GPixmap *src, const GRect *pdr) +{ + // check arguments + int srcwidth = src->columns(); + int srcheight = src->rows(); + int destwidth = (srcwidth * 3 + 1 ) / 2; + int destheight = (srcheight * 3 + 1) / 2; + GRect rect(0, 0, destwidth, destheight); + if (pdr != 0) + { + if (pdr->xmin < rect.xmin || + pdr->ymin < rect.ymin || + pdr->xmax > rect.xmax || + pdr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow4") ); + rect = *pdr; + destwidth = rect.width(); + destheight = rect.height(); + } + // initialize pixmap + init(destheight, destwidth, 0); + + // compute bounds + int dxz, dy; // location of bottomleft block in destination image + int sxz, sy; // location of bottomleft block in source image + euclidian_ratio(rect.ymin, 3, sy, dy); + euclidian_ratio(rect.xmin, 3, sxz, dxz); + sxz = 2 * sxz; + sy = 2 * sy; + dxz = - dxz; + dy = - dy; + + // prepare variables + int sadd = src->rowsize(); + int dadd = this->rowsize(); + const GPixel *sptr = (*src)[0] + sy * sadd; + GPixel *dptr = (*this)[0] + dy * dadd; + int s2add = 2 * sadd; + int d3add = 3 * dadd; + + // iterate over row blocks + while (dy < destheight) + { + int sx = sxz; + int dx = dxz; + // iterate over column blocks + while (dx < destwidth) + { + GPixel xin[4], xout[9]; + + if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) + { + if (sx+2<=srcwidth && sy+2<=srcheight) + { + upsample_2x2_to_3x3( sptr+sx, sadd, dptr+dx, dadd); + } + else + { + copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); + upsample_2x2_to_3x3(xin, 2, dptr+dx, dadd); + } + } + else + { + if (sx+2<=srcwidth && sy+2<=srcheight) + { + upsample_2x2_to_3x3( sptr+sx, sadd, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); + } + else + { + copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); + upsample_2x2_to_3x3(xin, 2, xout, 3); + copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); + } + } + // next column + dx += 3; + sx += 2; + } + // next row + dy += 3; + dptr += d3add; + sy += 2; + sptr += s2add; + } +} + + +////////////////////////////////////////////////// +// Blitting and attenuating +////////////////////////////////////////////////// + + +static unsigned char clip[512]; +static bool clipok = false; + +static void +compute_clip() +{ + clipok = true; + for (unsigned int i=0; i<sizeof(clip); i++) + clip[i] = (i<256 ? i : 255); +} + + +void +GPixmap::attenuate(const GBitmap *bm, int xpos, int ypos) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=0; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = 0; + dst[x].g = 0; + dst[x].r = 0; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (dst[x].b * level) >> 16; + dst[x].g -= (dst[x].g * level) >> 16; + dst[x].r -= (dst[x].r * level) >> 16; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + } +} + + +void +GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixel *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!clipok) compute_clip(); + if (!color) return; + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + unsigned char gr = color->r; + unsigned char gg = color->g; + unsigned char gb = color->b; + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = clip[dst[x].b + gb]; + dst[x].g = clip[dst[x].g + gg]; + dst[x].r = clip[dst[x].r + gr]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b = clip[dst[x].b + ((gb * level) >> 16)]; + dst[x].g = clip[dst[x].g + ((gg * level) >> 16)]; + dst[x].r = clip[dst[x].r + ((gr * level) >> 16)]; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + } +} + + +void +GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); + if (!clipok) compute_clip(); + if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) + G_THROW( ERR_MSG("GPixmap.diff_size") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = clip[dst[x].b + src2[x].b]; + dst[x].g = clip[dst[x].g + src2[x].g]; + dst[x].r = clip[dst[x].r + src2[x].r]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b = clip[dst[x].b + ((src2[x].b * level) >> 16)]; + dst[x].g = clip[dst[x].g + ((src2[x].g * level) >> 16)]; + dst[x].r = clip[dst[x].r + ((src2[x].r * level) >> 16)]; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + src2 += color->rowsize(); + } +} + + + +void +GPixmap::blend(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); + if (!clipok) compute_clip(); + if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) + G_THROW( ERR_MSG("GPixmap.diff_size") ); + // Compute number of rows and columns + int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), + xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); + if(xrows <= 0 || xcolumns <= 0) + return; + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); + const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); + GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = src2[x].b; + dst[x].g = src2[x].g; + dst[x].r = src2[x].r; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (((int)dst[x].b - (int)src2[x].b) * level) >> 16; + dst[x].g -= (((int)dst[x].g - (int)src2[x].g) * level) >> 16; + dst[x].r -= (((int)dst[x].r - (int)src2[x].r) * level) >> 16; + } + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + src2 += color->rowsize(); + } +} + + + + +void +GPixmap::stencil(const GBitmap *bm, + const GPixmap *pm, int pms, const GRect *pmr, + double corr) +{ + // Check arguments + GRect rect(0, 0, pm->columns()*pms, pm->rows()*pms); + if (pmr != 0) + { + if (pmr->xmin < rect.xmin || + pmr->ymin < rect.ymin || + pmr->xmax > rect.xmax || + pmr->ymax > rect.ymax ) + G_THROW( ERR_MSG("GPixmap.overflow5") ); + rect = *pmr; + } + // Compute number of rows + int xrows = nrows; + if ((int)bm->rows() < xrows) + xrows = bm->rows(); + if (rect.height() < xrows) + xrows = rect.height(); + // Compute number of columns + int xcolumns = ncolumns; + if ((int)bm->columns() < xcolumns) + xcolumns = bm->columns(); + if (rect.width() < xcolumns) + xcolumns = rect.width(); + // Precompute multiplier map + unsigned int multiplier[256]; + unsigned int maxgray = bm->get_grays() - 1; + for (unsigned int i=1; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Prepare color correction table + unsigned char gtable[256]; + color_correction_table_cache(corr, gtable); + // Compute starting point in blown up foreground pixmap + int fgy, fgy1, fgxz, fgx1z; + euclidian_ratio(rect.ymin, pms, fgy, fgy1); + euclidian_ratio(rect.xmin, pms, fgxz, fgx1z); + const GPixel *fg = (*pm)[fgy]; + const unsigned char *src = (*bm)[0]; + GPixel *dst = (*this)[0]; + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + int fgx = fgxz; + int fgx1 = fgx1z; + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 0) + { + if (srcpix >= maxgray) + { + dst[x].b = gtable[fg[fgx].b]; + dst[x].g = gtable[fg[fgx].g]; + dst[x].r = gtable[fg[fgx].r]; + } + else + { + unsigned int level = multiplier[srcpix]; + dst[x].b -= (((int)dst[x].b - (int)gtable[fg[fgx].b]) * level) >> 16; + dst[x].g -= (((int)dst[x].g - (int)gtable[fg[fgx].g]) * level) >> 16; + dst[x].r -= (((int)dst[x].r - (int)gtable[fg[fgx].r]) * level) >> 16; + } + } + // Next column + if (++fgx1 >= pms) + { + fgx1 = 0; + fgx += 1; + } + } + // Next line + dst += rowsize(); + src += bm->rowsize(); + if (++fgy1 >= pms) + { + fgy1 = 0; + fg += pm->rowsize(); + } + } +} + +GP<GPixmap> GPixmap::rotate(int count) +{ + GP<GPixmap> newpixmap(this); + if((count %= 4)) + { + if( count&0x01) + newpixmap = new GPixmap(ncolumns, nrows); + else + newpixmap = new GPixmap(nrows, ncolumns); + + GPixmap &dpixmap = *newpixmap; + + GMonitorLock lock(&pixmap_monitor()); + switch(count) + { + case 1: //// rotate 90 counter clockwise + { + int lastrow = dpixmap.rows()-1; + + for(int y=0; y<nrows; y++) + { + const GPixel *r=operator [] (y); + for(int x=0,xnew=lastrow; xnew>=0; x++,xnew--) + { + dpixmap[xnew][y] = r[x]; + } + } + } + break; + case 2: //// rotate 180 counter clockwise + { + int lastrow = dpixmap.rows()-1; + int lastcolumn = dpixmap.columns()-1; + + for(int y=0,ynew=lastrow; ynew>=0; y++,ynew--) + { + const GPixel *r=operator [] (y); + GPixel *d=dpixmap[ynew]; + for(int xnew=lastcolumn; xnew>=0; r++,xnew--) + { + d[xnew] = *r; + } + } + } + break; + case 3: //// rotate 270 counter clockwise + { + int lastcolumn = dpixmap.columns()-1; + + for(int y=0,ynew=lastcolumn; ynew>=0; y++,ynew--) + { + const GPixel *r=operator [] (y); + for(int x=0; x<ncolumns; x++) + { + dpixmap[x][ynew] = r[x]; + } + } + } + break; + } + } + return newpixmap; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GPixmap.h b/kviewshell/plugins/djvu/libdjvu/GPixmap.h new file mode 100644 index 00000000..32d51c7e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GPixmap.h @@ -0,0 +1,531 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GPixmap.h,v 1.8 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GPIXMAP_H_ +#define _GPIXMAP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GPixmap.h + + Files #"GPixmap.h"# and #"GPixmap.cpp"# implement class \Ref{GPixmap}. + Instances of this class represent color images. Each RGB pixel is + represented by structure \Ref{GPixel}. The ``bottom left'' coordinate system + is used consistently in the DjVu library. Line zero of a GPixmap is the + bottom line in the color image. Pixels are organized from left to right + within each line. + + {\bf ToDo} --- More sophisticated color correction schemes. + + @memo + Generic support for color images. + @author + L\'eon Bottou <[email protected]> + @version + #$Id: GPixmap.h,v 1.8 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class GBitmap; +class GRect; +class ByteStream; + + +/** Color pixel as a RGB triple. + The colors are represented using three bytes named #r#, #g# and #b#. The + value of these bytes represent additive amounts of light. Color white is + represented by setting all three bytes to #255#. Color black is + represented by setting all three bytes to #0#. This convention should not + be confused with the convention adopted for class \Ref{GBitmap} where the + pixel values represent an ink level. */ + +struct GPixel +{ + /** Blue component. */ + unsigned char b; + /** Green component. */ + unsigned char g; + /** Red component. */ + unsigned char r; + /** Returns true iff colors are identical. */ + friend int operator==(const GPixel & p1, const GPixel & p2); + /** Returns true iff colors are different. */ + friend int operator!=(const GPixel & p1, const GPixel & p2); + /** Returns a hash code for the color. */ + friend unsigned int hash(const GPixel &p); + /** @name Predefined colors. */ + //@{ + /// GPixel::WHITE is initialized to #rgb:255/255/255#. + static const GPixel WHITE; + /// GPixel::BLACK is initialized to #rgb:0/0/0#. + static const GPixel BLACK; + /// GPixel::BLUE is initialized to #rgb:0/0/255#. + static const GPixel BLUE; + /// GPixel::GREEN is initialized to #rgb:0/255/0#. + static const GPixel GREEN; + /// GPixel::RED is initialized to #rgb:255/0/0#. + static const GPixel RED; + //@} +}; + + +/** RGB Color images. + Instances of class #GPixmap# represent color images as a two dimensional + array of pixels \Ref{GPixel}. The bracket operator returns a pointer to + the pixels composing one line of the image. This pointer can be used as + an array to read or write the pixels of this particular line. Following + the general convention of the DjVu Reference Library, line zero is always + the bottom line of the image. + */ + +class GPixmap : public GPEnabled +{ +protected: + GPixmap(void); + GPixmap(int nrows, int ncolumns, const GPixel *filler=0); + GPixmap(const GBitmap &ref); + GPixmap(const GBitmap &ref, const GRect &rect); + GPixmap(const GPixmap &ref); + GPixmap(const GPixmap &ref, const GRect &rect); + GPixmap(ByteStream &ref); + +public: + /// Virtual destructor. + virtual ~GPixmap(); + + void destroy(void); + /** @name Construction. */ + //@{ + /** Creates an empty GBitmap object. The returned GPixmap has zero rows + and zero columns. Use function \Ref{init} to change the size of the + image. */ + static GP<GPixmap> create(void) {return new GPixmap();} + + /** Creates a GPixmap with #nrows# rows and #ncolumns# columns. When the + optional argument #filler# is specified, all pixels are initialized + with the corresponding color. */ + static GP<GPixmap> create( + const int nrows, const int ncolumns, const GPixel *filler=0) + { return new GPixmap(nrows,ncolumns,filler); } + + /** Creates a GPixmap by copying the gray level image #ref#. + The constructed GPixmap has the same size as #ref#. The pixels + are initialized with shades of grays copied from #ref#. */ + static GP<GPixmap> create(const GBitmap &ref) + { return new GPixmap(ref); } + + /** Creates a GPixmap by copying the rectangle #rect# of the gray level + image #ref#. The constructed GPixmap has the same size as rectangle + #rect#. The pixels are initialized with shades of grays converted from + the ink levels represented in #ref#. This conversion depends on the + number of gray levels in #ref#. */ + static GP<GPixmap> create(const GBitmap &ref, const GRect &rect) + { return new GPixmap(ref,rect); } + + /** Copy constructors. Creates a GPixmap by replicating the size and the + contents of GPixmap #ref#. */ + static GP<GPixmap> create(const GPixmap &ref) + { return new GPixmap(ref); } + + /** Creates a GPixmap by copying the rectangle #rect# of the color image #ref#. + The constructed GPixmap has the same size as rectangle #rect#. + The pixels are initialized with colors copied from #ref#. */ + static GP<GPixmap> create(const GPixmap &ref, const GRect &rect) + { return new GPixmap(ref,rect); } + + /** Creates a GPixmap by reading PPM data from ByteStream #ref#. + See \Ref{PNM and RLE file formats} for more information. */ + static GP<GPixmap> create(ByteStream &ref) + { return new GPixmap(ref); } + + //@} + + /** @name Initialization. */ + //@{ + /** Resets the GPixmap to #nrows# rows and #ncolumns# columns. When the + optional argument #filler# is specified, all pixels are initialized with + the corresponding color. The previous content of the GPixmap is discarded. */ + void init(int nrows, int ncolumns, const GPixel *filler=0); + /** Resets the GPixmap by copying the size and the contents of the color + image #ref#. The previous content of the GPixmap is discarded. */ + void init(const GPixmap &ref); + /** Resets the GPixmap by copying the rectangle #rect# of the color image #ref#. + The previous content of the GPixmap is discarded. */ + void init(const GPixmap &ref, const GRect &rect); + /** Resets the GPixmap by copying the size and the contents of the gray + level image #ref#. The optional argument #ramp# is an array of 256 + pixel values used for mapping the gray levels to color values. + Setting #ramp# to zero selects a linear ramp of shades of gray. */ + void init(const GBitmap &ref, const GPixel *ramp=0); + /** Resets the GPixmap by copying the rectangle #rect# of the gray level + image #ref#. The optional argument #ramp# is an array of 256 pixel + values used for mapping the gray levels to color values. Setting #ramp# + to zero selects a linear ramp computed according to the maximal number + of gray levels in #ref#. */ + void init(const GBitmap &ref, const GRect &rect, const GPixel *ramp=0); + /** Resets the GPixmap by reading PPM data from ByteStream #ref#. See + \Ref{PNM and RLE file formats} for more information. */ + void init(ByteStream &ref); + /** Resets the GPixmap by copying the gray level image #ref#. The pixels + are initialized with shades of grays copied from #ref#. */ + GPixmap& operator=(const GBitmap &ref); + /** Copy operator. Resets the GPixmap by copying the size and the contents + of the color image #ref#. The previous content of the GPixmap is + discarded. */ + GPixmap& operator=(const GPixmap &ref); + //@} + + /** @name Accessing pixels. */ + //@{ + /** Returns the number of rows (the image height). */ + unsigned int rows() const; + /** Returns the number of columns (the image width). */ + unsigned int columns() const; + /** Returns a constant pointer to the first GPixel in row #row#. This + pointer can be used as an array to read the row elements. */ + const GPixel * operator[] (int row) const; + /** Returns a pointer to the first GPixel in row #row#. This pointer can be + used as an array to read or write the row elements. */ + GPixel * operator[] (int row); + /** Returns the length (in pixels) of a row in memory. This number is equal + to the difference between pointers to pixels located in the same column + in consecutive rows. This difference may be larger than the number of + columns in the image. */ + unsigned int rowsize() const; + //@} + + /** @name Resampling images. */ + //@{ + /** Resets this GPixmap with a subsampled segment of color image #src#. + This function conceptually rescales image #src# by a factor #1:factor#, + and copies rectangle #rect# of the subsampled image into the current GPixmap. + The full subsampled image is copied if #rect# is a null pointer. + Both operations are however performed together for efficiency reasons. + Subsampling works by averaging the colors of the source pixels located + in small squares of size #factor# times #factor#. */ + void downsample(const GPixmap *src, int factor, const GRect *rect=0); + /** Resets this GPixmap with a oversampled segment of color image #src#. + This function conceptually rescales image #src# by a factor #factor:1#, + and copies rectangle #rect# of the oversampled image into the current + GPixmap. The full oversampled image is copied if #rect# is a null + pointer. Both operations are however performed together for efficiency + reasons. Oversampling works by replicating the color of the source + pixels into squares of size #factor# times #factor#. */ + void upsample(const GPixmap *src, int factor, const GRect *rect=0); + /** Resets this GPixmap with a rescaled segment of #src# (zoom 75%). This + function conceptually rescales image #src# by a factor #3:4#, and copies + rectangle #rect# of the rescaled image into the current GPixmap. The + full rescaled image is copied if #rect# is a null pointer. Both + operations are however performed together for efficiency reasons. This + function has been superseded by class \Ref{GPixmapScaler}. */ + void downsample43(const GPixmap *src, const GRect *rect=0); + /** Resets this GPixmap with a rescaled segment of #src# (zoom 150%). This + function conceptually rescales image #src# by a factor #3:2# and copies + rectangle #rect# of the rescaled image into the current GPixmap. The + full rescaled image is copied if #rect# is a null pointer. Both + operations are however performed together for efficiency reasons. This + function has been superseded by class \Ref{GPixmapScaler}. */ + void upsample23(const GPixmap *src, const GRect *rect=0); + //@} + + /** @name Blitting and applying stencils. + These function is essential for rendering DjVu images. The elementary + functions are \Ref{attenuate} and \Ref{blit}. The combined functions + \Ref{blend} and \Ref{stencil} should be viewed as optimizations. */ + //@{ + /** Attenuates the color image in preparation for a blit. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then multiplied by #1.0-Alpha# where + #Alpha# denotes the gray value, in range #[0,1]#, represented by the + corresponding pixel of bitmap #bm#. */ + void attenuate(const GBitmap *bm, int x, int y); + /** Blits solid color #color# through transparency mask #bm#. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then modified by adding color + #color# multiplied by #Alpha#, where #Alpha# denotes the gray value, in + range #[0,1]#, represented by the corresponding pixel of bitmap #bm#. */ + void blit(const GBitmap *bm, int x, int y, const GPixel *color); + /** Blits pixmap #color# through transparency mask #bm#. + Bitmap #bm# is positionned at location #x#,#y# over this color image. + The matching color image pixels are then modified by adding the + corresponding pixel color in pixmap #color#, multiplied by #Alpha#, + where #Alpha# denotes the gray value, in range #[0,1]#, represented by + the corresponding pixel of bitmap #bm#. */ + void blit(const GBitmap *bm, int x, int y, const GPixmap *color); + /** Performs alpha blending. This function is similar to first calling + \Ref{attenuate} with alpha map #bm# and then calling \Ref{blit} with + alpha map #bm# and color map #color#. Both operations are performed + together for efficiency reasons. */ + void blend(const GBitmap *bm, int x, int y, const GPixmap *color); + /** Resample color pixmap and performs color corrected alpha blending. This + function conceptually computes an intermediate color image by first + upsampling the GPixmap #pm# by a factor #pms:1# (see \Ref{upsample}), + extracting the sub-image designated by rectangle #pmr# and applying + color correction #corr# (see \Ref{color_correct}). This intermediate + color image is then blended into this pixel map according to the alpha + map #bm# (see \Ref{blend}). */ + void stencil(const GBitmap *bm, + const GPixmap *pm, int pms, + const GRect *pmr, double corr=1.0); + //@} + + /** @name Manipulating colors. */ + //@{ + /** Dithers the image to 216 colors. This function applies an ordered + dithering algorithm to reduce the image to 216 predefined colors. These + predefined colors are located on a color cube of 6x6x6 colors: the color + RGB coordinates can only take the following values: #0#, #51#, #102#, + #163#, #214# or #255#. This is useful for displaying images on a device + supporting a maximum of 256 colors. Arguments #xmin# and #ymin# control + the position of the dithering grids. This is useful for dithering tiled + images. Arguments #xmin# and #ymin# must be the position of the bottom + left corner of the tile contained in this GPixmap. Properly setting + these arguments eliminates dithering artifacts on the tile + boundaries. */ + void ordered_666_dither(int xmin=0, int ymin=0); + /** Dithers the image to 32768 colors. This function applies an ordered + dithering algorithm to reduce the image to 32768 predefined colors. + These predefined colors are located on a color cube of 32x32x32 colors: + the color RGB coordinates can only take values in which the three least + significant bits are set to #1#. This is useful for displaying images + with less than 24 bits per pixel. Arguments #xmin# and #ymin# control + the position of the dithering grids. This is useful for dithering tiled + images. Arguments #xmin# and #ymin# must be the position of the bottom + left corner of the tile contained in this GPixmap. Properly setting + these arguments eliminates dithering artifacts on the tile + boundaries. */ + void ordered_32k_dither(int xmin=0, int ymin=0); + /** Applies a luminance gamma correction factor of #corr#. Values greater than + #1.0# make the image brighter. Values smaller than #1.0# make the image + darker. The documentation of program \Ref{ppmcoco} explains how to + properly use this function. */ + void color_correct(double corr); + /** Applies a luminance gamma correction to an array of pixels. + This function is {\em static} and does not modify this pixmap. */ + static void color_correct(double corr, GPixel *pixels, int npixels); + + //@} + + /** @name Miscellaneous. */ + //@{ + /** Returns the number of bytes allocated for this image. */ + inline unsigned int get_memory_usage() const; + /** Saves the image into ByteStream #bs# using the PPM format. + Argument #raw# selects the ``Raw PPM'' (1) or the ``Ascii PPM'' (0) format. + See \Ref{PNM and RLE file formats} for more information. */ + void save_ppm(ByteStream &bs, int raw=1) const; + //@} + + /** @name Stealing or borrowing the memory buffer (advanced). */ + //@{ + /** Steals the memory buffer of a GPixmap. This function returns the + address of the memory buffer allocated by this GPixmap object. The + offset of the first pixel in the bottom line is written into variable + #offset#. Other lines can be accessed using pointer arithmetic (see + \Ref{rowsize}). The GPixmap object no longer ``owns'' the buffer: you + must explicitly de-allocate the buffer using #operator delete []#. This + de-allocation should take place after the destruction or the + re-initialization of the GPixmap object. This function will return a + null pointer if the GPixmap object does not ``own'' the buffer in the + first place. */ + GPixel *take_data(size_t &offset); + /** Initializes this GPixmap by borrowing a memory segment. The GPixmap + then directly addresses the memory buffer #data# provided by the user. + This buffer must be large enough to hold #w*h# GPixels. The GPixmap + object does not ``own'' the buffer: you must explicitly de-allocate the + buffer using #operator delete []#. This de-allocation should take place + after the destruction or the re-initialization of the GPixmap object. */ + inline void borrow_data(GPixel &data, int w, int h); + /// Identical to the above, but GPixmap will do the delete []. + void donate_data(GPixel *data, int w, int h); + + /** Rotates pixmap by 90, 180 or 270 degrees anticlockwise + and returns a new pixmap, input pixmap is not changed. + count can be 1, 2, or 3 for 90, 180, 270 degree rotation. + It returns the same pixmap if not rotated. */ + GP<GPixmap> rotate(int count=0); + + //@} + + // Please ignore these two functions. Their only purpose is to allow + // DjVu viewer compile w/o errors. eaf. + // Is this still useful ?. lyb. + int get_grays(void) const { return 256; }; + void set_grays(int) {};\ + +protected: + // data + unsigned short nrows; + unsigned short ncolumns; + unsigned short nrowsize; + GPixel *pixels; + GPixel *pixels_data; + friend class DjVu_PixImage; +}; + +//@} + +// INLINE -------------------------- + + +inline int +operator==(const GPixel & p1, const GPixel & p2) +{ + return p1.r==p2.r && p1.g==p2.g && p1.b==p2.b; +} + +inline int +operator!=(const GPixel & p1, const GPixel & p2) +{ + return p1.r!=p2.r || p1.g!=p2.g || p1.b!=p2.b; +} + +inline unsigned int +hash(const GPixel &p) +{ + unsigned int x = (p.b<<16)|(p.g<<8)|(p.r); + return x ^ (p.b<<4) ^ (p.r<<12); +} + +inline unsigned int +GPixmap::rows() const +{ + return nrows; +} + +inline unsigned int +GPixmap::columns() const +{ + return ncolumns; +} + +inline unsigned int +GPixmap::rowsize() const +{ + return nrowsize; +} + +inline GPixel * +GPixmap::operator[](int row) +{ + if (row<0 || row>=nrows || !pixels) return 0; + return &pixels[row * nrowsize]; +} + +inline const GPixel * +GPixmap::operator[](int row) const +{ + if (row<0 || row>=nrows) return 0; + return &pixels[row * nrowsize]; +} + +inline GPixmap & +GPixmap::operator=(const GBitmap &ref) +{ + init(ref); + return *this; +} + +inline GPixmap & +GPixmap::operator=(const GPixmap &ref) +{ + init(ref); + return *this; +} + +inline void +GPixmap::borrow_data(GPixel &data, int w, int h) +{ + donate_data(&data,w,h); + pixels_data=0; +} + +////////////////////////////////////////////////// +// Memory usage +////////////////////////////////////////////////// + + +inline unsigned int +GPixmap::get_memory_usage() const +{ + return sizeof(GPixmap)+(nrows * ncolumns * sizeof(GPixel)); +} + +// --------------------------------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/GRect.cpp b/kviewshell/plugins/djvu/libdjvu/GRect.cpp new file mode 100644 index 00000000..1ac0a87c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GRect.cpp @@ -0,0 +1,458 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GRect.cpp,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implementation of class GRect and GRectMapper +// - Author: Leon Bottou, 05/1997 + + +#include "GRect.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +// -- Local utilities + +static inline int +imin(int x, int y) +{ + if (x < y) + return x; + else + return y; +} + +static inline int +imax(int x, int y) +{ + if (x > y) + return x; + else + return y; +} + +static inline void +iswap(int &x, int &y) +{ + int tmp = x; x = y; y = tmp; +} + +// -- Class GRect + +int +operator==(const GRect & r1, const GRect & r2) +{ + int isempty1 = r1.isempty(); + int isempty2 = r2.isempty(); + if (isempty1 || isempty2) + if (isempty1 && isempty2) + return 1; + if ( r1.xmin==r2.xmin && r1.xmax==r2.xmax + && r1.ymin==r2.ymin && r1.ymax==r2.ymax ) + return 1; + return 0; +} + +int +GRect::inflate(int dx, int dy) +{ + xmin -= dx; + xmax += dx; + ymin -= dy; + ymax += dy; + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::translate(int dx, int dy) +{ + xmin += dx; + xmax += dx; + ymin += dy; + ymax += dy; + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::intersect(const GRect &rect1, const GRect &rect2) +{ + xmin = imax(rect1.xmin, rect2.xmin); + xmax = imin(rect1.xmax, rect2.xmax); + ymin = imax(rect1.ymin, rect2.ymin); + ymax = imin(rect1.ymax, rect2.ymax); + if (! isempty()) + return 1; + xmin = ymin = xmax = ymax = 0; + return 0; +} + +int +GRect::recthull(const GRect &rect1, const GRect &rect2) +{ + if (rect1.isempty()) + { + xmin = rect2.xmin; + xmax = rect2.xmax; + ymin = rect2.ymin; + ymax = rect2.ymax; + return !isempty(); + } + if (rect2.isempty()) + { + xmin = rect1.xmin; + xmax = rect1.xmax; + ymin = rect1.ymin; + ymax = rect1.ymax; + return !isempty(); + } + xmin = imin(rect1.xmin, rect2.xmin); + xmax = imax(rect1.xmax, rect2.xmax); + ymin = imin(rect1.ymin, rect2.ymin); + ymax = imax(rect1.ymax, rect2.ymax); + return 1; +} + +int +GRect::contains(const GRect & rect) const +{ + GRect tmp_rect; + tmp_rect.intersect(*this, rect); + return tmp_rect==rect; +} + +void +GRect::scale(float factor) +{ + xmin = (int)(((float)xmin) * factor); + ymin = (int)(((float)ymin) * factor); + xmax = (int)(((float)xmax) * factor); + ymax = (int)(((float)ymax) * factor); +} + +void +GRect::scale(float xfactor, float yfactor) +{ + xmin = (int)(((float)xmin) * xfactor); + ymin = (int)(((float)ymin) * yfactor); + xmax = (int)(((float)xmax) * xfactor); + ymax = (int)(((float)ymax) * yfactor); +} +// -- Class GRatio + + +inline +GRectMapper::GRatio::GRatio() + : p(0), q(1) +{ +} + +inline +GRectMapper::GRatio::GRatio(int p, int q) + : p(p), q(q) +{ + if (q == 0) + G_THROW( ERR_MSG("GRect.div_zero") ); + if (p == 0) + q = 1; + if (q < 0) + { + p = -p; + q = -q; + } + int gcd = 1; + int g1 = p; + int g2 = q; + if (g1 > g2) + { + gcd = g1; + g1 = g2; + g2 = gcd; + } + while (g1 > 0) + { + gcd = g1; + g1 = g2 % g1; + g2 = gcd; + } + p /= gcd; + q /= gcd; +} + + +#ifdef HAVE_LONG_LONG_INT +#define llint_t long long int +#else +#define llint_t long int +#endif + +inline int +operator*(int n, GRectMapper::GRatio r ) +{ + /* [LB] -- This computation is carried out with integers and + rational numbers because it must be exact. Some lizard changed + it to double and this is wrong. I suspect they did so because + they encountered overflow issues. Let's use long long ints. */ + llint_t x = (llint_t) n * (llint_t) r.p; + if (x >= 0) + return ((r.q/2 + x) / r.q); + else + return - ((r.q/2 - x) / r.q); +} + +inline int +operator/(int n, GRectMapper::GRatio r ) +{ + /* [LB] -- See comment in operator*() above. */ + llint_t x = (llint_t) n * (llint_t) r.q; + if (x >= 0) + return ((r.p/2 + x) / r.p); + else + return - ((r.p/2 - x) / r.p); +} + + +// -- Class GRectMapper + +#define MIRRORX 1 +#define MIRRORY 2 +#define SWAPXY 4 + + +GRectMapper::GRectMapper() +: rectFrom(0,0,1,1), + rectTo(0,0,1,1), + code(0) +{ + +} + +void +GRectMapper::clear() +{ + rectFrom = GRect(0,0,1,1); + rectTo = GRect(0,0,1,1); + code = 0; +} + +void +GRectMapper::set_input(const GRect &rect) +{ + if (rect.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect1") ); + rectFrom = rect; + if (code & SWAPXY) + { + iswap(rectFrom.xmin, rectFrom.ymin); + iswap(rectFrom.xmax, rectFrom.ymax); + } + rw = rh = GRatio(); +} + +void +GRectMapper::set_output(const GRect &rect) +{ + if (rect.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect2") ); + rectTo = rect; + rw = rh = GRatio(); +} + +void +GRectMapper::rotate(int count) +{ + int oldcode = code; + switch (count & 0x3) + { + case 1: + code ^= (code & SWAPXY) ? MIRRORY : MIRRORX; + code ^= SWAPXY; + break; + case 2: + code ^= (MIRRORX|MIRRORY); + break; + case 3: + code ^= (code & SWAPXY) ? MIRRORX : MIRRORY; + code ^= SWAPXY; + break; + } + if ((oldcode ^ code) & SWAPXY) + { + iswap(rectFrom.xmin, rectFrom.ymin); + iswap(rectFrom.xmax, rectFrom.ymax); + rw = rh = GRatio(); + } +} + +void +GRectMapper::mirrorx() +{ + code ^= MIRRORX; +} + +void +GRectMapper::mirrory() +{ + code ^= MIRRORY; +} + +void +GRectMapper::precalc() +{ + if (rectTo.isempty() || rectFrom.isempty()) + G_THROW( ERR_MSG("GRect.empty_rect3") ); + rw = GRatio(rectTo.width(), rectFrom.width()); + rh = GRatio(rectTo.height(), rectFrom.height()); +} + +void +GRectMapper::map(int &x, int &y) +{ + int mx = x; + int my = y; + // precalc + if (! (rw.p && rh.p)) + precalc(); + // swap and mirror + if (code & SWAPXY) + iswap(mx,my); + if (code & MIRRORX) + mx = rectFrom.xmin + rectFrom.xmax - mx; + if (code & MIRRORY) + my = rectFrom.ymin + rectFrom.ymax - my; + // scale and translate + x = rectTo.xmin + (mx - rectFrom.xmin) * rw; + y = rectTo.ymin + (my - rectFrom.ymin) * rh; +} + +void +GRectMapper::unmap(int &x, int &y) +{ + // precalc + if (! (rw.p && rh.p)) + precalc(); + // scale and translate + int mx = rectFrom.xmin + (x - rectTo.xmin) / rw; + int my = rectFrom.ymin + (y - rectTo.ymin) / rh; + // mirror and swap + if (code & MIRRORX) + mx = rectFrom.xmin + rectFrom.xmax - mx; + if (code & MIRRORY) + my = rectFrom.ymin + rectFrom.ymax - my; + if (code & SWAPXY) + iswap(mx,my); + x = mx; + y = my; +} + +void +GRectMapper::map(GRect &rect) +{ + map(rect.xmin, rect.ymin); + map(rect.xmax, rect.ymax); + if (rect.xmin >= rect.xmax) + iswap(rect.xmin, rect.xmax); + if (rect.ymin >= rect.ymax) + iswap(rect.ymin, rect.ymax); +} + +void +GRectMapper::unmap(GRect &rect) +{ + unmap(rect.xmin, rect.ymin); + unmap(rect.xmax, rect.ymax); + if (rect.xmin >= rect.xmax) + iswap(rect.xmin, rect.xmax); + if (rect.ymin >= rect.ymax) + iswap(rect.ymin, rect.ymax); +} + +GRect +GRectMapper::get_input() +{ + return rectFrom; +} + +GRect +GRectMapper::get_output() +{ + return rectTo; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GRect.h b/kviewshell/plugins/djvu/libdjvu/GRect.h new file mode 100644 index 00000000..1a86f80d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GRect.h @@ -0,0 +1,407 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GRect.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GRECT_H_ +#define _GRECT_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name GRect.h + Files #"GRect.h"# and #"GRect.cpp"# implement basic operations on + rectangles. Class \Ref{GRect} is used to represent rectangles. Class + \Ref{GRectMapper} represent the correspondence between points relative to + given rectangles. Class \Ref{GRatio} is used to represent scaling factors + as rational numbers. + @memo + Rectangle manipulation class. + @author + L\'eon Bottou <[email protected]> -- initial implementation. + @version + #$Id: GRect.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** @name Point Coordinates vs. Pixel Coordinates + + The DjVu technology relies on the accurate superposition of images at + different resolutions. Such an accuracy cannot be reached with the usual + assumption that pixels are small enough to be considered infinitesimally + small. We must distinguish very precisely ``points'' and ``pixels''. + This distinction is essential for performing scaling operations. + + The pixels of an image are identified by ``pixel coordinates''. The + bottom-left corner pixel has coordinates #(0,0)# and the top-right corner + pixel has coordinates #(w-1,h-1)# where #w# and #h# are the image size. + Pixel coordinates are necessarily integers since pixels never overlap. + + An infinitesimally small point is identified by its ``point coordinates''. + There may be fractional point coordinates, although this library does not + make use of them. Points with integer coordinates are located {\em on the + corners of each pixel}. They are not located on the pixel centers. The + center of the pixel with pixel coordinates #(i,j)# is located at point + coordinates #(i+1/2,j+1/2)#. In other words, the pixel #(i,j)# extends + from point #(i,j)# to point #(i+1,j+1)#. + + Therefore, the point located on the bottom left corner of an image has + coordinates #(0,0)#. This point is in fact the bottom left corner of the + bottom left pixel of the image. The point located on the top right corner + of an image has coordinates #(w,h)# where #w# and #h# are the image size. + This is in fact the top right corner of pixel #(w-1,h-1)# which is the + image pixel with the highest coordinates. +*/ +//@{ +//@} + + + +/** Rectangle class. Each instance of this class represents a rectangle whose + sides are parallel to the axis. Such a rectangle represents all the points + whose coordinates lies between well defined minimal and maximal values. + Member functions can combine several rectangles by computing the + intersection of rectangles (\Ref{intersect}) or the smallest rectangle + enclosing two rectangles (\Ref{recthull}). */ + +class GRect +{ +public: + /** #OrientationBits# defines 3 mutually exclusive + bits to indicate the image orientation. + + There are four possible rotation values for an image + which are 0 degrees, 90 degrees, 180 degrees, and 270 degrees. + In addition the image can be mirrored backwards in any of these + orientations, giving a possible of 8 orientations. To sanely deal + with these orientations, we have defined 3 mutually exclusive + bits. These are BOTTOM_UP, MIRROR, and ROTATE90_CW. + */ + enum OrientationBits + { + BOTTOM_UP=0x1, /* Upside down */ + MIRROR=0x2, /* Written backwards. (right to left) */ + ROTATE90_CW=0x4 /* rotated 90 degrees */ + }; + + /** #Orientations# defines all 8 possible orientations, using + the three \Ref{OrientationBits}. + \begin{itemize} + \item {\em TDLRNR} for Top Down, Left to Right, No Rotation. + \item {\em BULRNR} for Bottom Up, Left to Right, No Rotation. + \item {\em TDRLNR} for Top Down, Right to Left, No Rotation. + \item {\em BURLNR} for Bottom Up, Right to Left, No Rotation. + \item {\em TDLRCW} for Top Down, Left to Right, 90 degree CW rotation. + \item {\em BULRCW} for Bottom Up, Left to Right, 90 degree CW rotation. + \item {\em TDRLCW} for Top Down, Right to Left, 90 degree CW rotation. + \item {\em BURLCW} for Bottom Up, Right to Left, 90 degree CW rotation. + \end{itemize} + */ + enum Orientations + { + TDLRNR=0, /* normal orientation */ + BULRNR=BOTTOM_UP, /* upside down */ + TDRLNR=MIRROR, /* backwards (right to left) */ + BURLNR=MIRROR|BOTTOM_UP, /* rotate 180 */ + TDLRCW=ROTATE90_CW, /* rotated 90 */ + BULRCW=ROTATE90_CW|BOTTOM_UP, /* backwards and rotate 180 */ + TDRLCW=ROTATE90_CW|MIRROR, /* backwards and rotate 90 */ + BURLCW=ROTATE90_CW|MIRROR|BOTTOM_UP /* rotate 270 */ + }; + + static Orientations + rotate(const int angle,Orientations orientation) + { + for(int a=(((angle)%360)+405)%360;a>90;a-=90) + orientation=(Orientations)((int)orientation^(int)(orientation&ROTATE90_CW)?BURLCW:TDLRCW); + return orientation; + } + + static int + findangle(const Orientations orientation) + { + int a=270; + while(a&&(rotate(a,BURLNR)!=orientation)&&(rotate(a,TDRLNR)!=orientation)) + a-=90; + return a; + } + + /** Constructs an empty rectangle */ + GRect(); + /** Constructs a rectangle given its minimal coordinates #xmin# and #ymin#, + and its measurements #width# and #height#. Setting #width# or #height# to zero + produces an empty rectangle. */ + GRect(int xmin, int ymin, unsigned int width=0, unsigned int height=0); + /** Returns the rectangle width. */ + int width() const; + /** Returns the rectangle height. */ + int height() const; + /** Returns the area of the rectangle. */ + int area() const; + /** Returns true if the rectangle is empty. */ + int isempty() const; + /** Returns true if the rectangle contains pixel (#x#,#y#). A rectangle + contains all pixels with horizontal pixel coordinates in range #xmin# + (inclusive) to #xmax# (exclusive) and vertical coordinates #ymin# + (inclusive) to #ymax# (exclusive). */ + int contains(int x, int y) const; + /** Returns true if this rectangle contains the passed rectangle #rect#. + The function basically checks, that the intersection of this rectangle + with #rect# is #rect#. */ + int contains(const GRect & rect) const; + /** Returns true if rectangles #r1# and #r2# are equal. */ + friend int operator==(const GRect & r1, const GRect & r2); + /** Returns true if rectangles #r1# and #r2# are not equal. */ + friend int operator!=(const GRect & r1, const GRect & r2); + /** Resets the rectangle to the empty rectangle */ + void clear(); + /** Fatten the rectangle. Both vertical sides of the rectangle are pushed + apart by #dx# units. Both horizontal sides of the rectangle are pushed + apart by #dy# units. Setting arguments #dx# (resp. #dy#) to a negative + value reduces the rectangle horizontal (resp. vertical) size. */ + int inflate(int dx, int dy); + /** Translate the rectangle. The new rectangle is composed of all the points + of the old rectangle translated by #dx# units horizontally and #dy# + units vertically. */ + int translate(int dx, int dy); + /** Sets the rectangle to the intersection of rectangles #rect1# and #rect2#. + This function returns true if the intersection rectangle is not empty. */ + int intersect(const GRect &rect1, const GRect &rect2); + /** Sets the rectangle to the smallest rectangle containing the points of + both rectangles #rect1# and #rect2#. This function returns true if the + created rectangle is not empty. */ + int recthull(const GRect &rect1, const GRect &rect2); + /** Multiplies xmin, ymin, xmax, ymax by factor and scales the rectangle*/ + void scale(float factor); + /** Multiplies xmin, xmax by xfactor and ymin, ymax by yfactor and scales the rectangle*/ + void scale(float xfactor, float yfactor); + /** Minimal horizontal point coordinate of the rectangle. */ + int xmin; + /** Minimal vertical point coordinate of the rectangle. */ + int ymin; + /** Maximal horizontal point coordinate of the rectangle. */ + int xmax; + /** Maximal vertical point coordinate of the rectangle. */ + int ymax; +}; + + +/** Maps points from one rectangle to another rectangle. This class + represents a relation between the points of two rectangles. Given the + coordinates of a point in the first rectangle (input rectangle), function + \Ref{map} computes the coordinates of the corresponding point in the + second rectangle (the output rectangle). This function actually implements + an affine transform which maps the corners of the first rectangle onto the + matching corners of the second rectangle. The scaling operation is + performed using integer fraction arithmetic in order to maximize + accuracy. */ +class GRectMapper +{ +public: + /** Constructs a rectangle mapper. */ + GRectMapper(); + /** Resets the rectangle mapper state. Both the input rectangle + and the output rectangle are marked as undefined. */ + void clear(); + /** Sets the input rectangle. */ + void set_input(const GRect &rect); + /** Returns the input rectangle. */ + GRect get_input(); + /** Sets the output rectangle. */ + void set_output(const GRect &rect); + /** Returns the output rectangle. */ + GRect get_output(); + /** Composes the affine transform with a rotation of #count# quarter turns + counter-clockwise. This operation essentially is a modification of the + match between the corners of the input rectangle and the corners of the + output rectangle. */ + void rotate(int count=1); + /** Composes the affine transform with a symmetry with respect to the + vertical line crossing the center of the output rectangle. This + operation essentially is a modification of the match between the corners + of the input rectangle and the corners of the output rectangle. */ + void mirrorx(); + /** Composes the affine transform with a symmetry with respect to the + horizontal line crossing the center of the output rectangle. This + operation essentially is a modification of the match between the corners + of the input rectangle and the corners of the output rectangle. */ + void mirrory(); + /** Maps a point according to the affine transform. Variables #x# and #y# + initially contain the coordinates of a point. This operation overwrites + these variables with the coordinates of a second point located in the + same position relative to the corners of the output rectangle as the + first point relative to the matching corners of the input rectangle. + Coordinates are rounded to the nearest integer. */ + void map(int &x, int &y); + /** Maps a rectangle according to the affine transform. This operation + consists in mapping the rectangle corners and reordering the corners in + the canonical rectangle representation. Variable #rect# is overwritten + with the new rectangle coordinates. */ + void map(GRect &rect); + /** Maps a point according to the inverse of the affine transform. + Variables #x# and #y# initially contain the coordinates of a point. This + operation overwrites these variables with the coordinates of a second + point located in the same position relative to the corners of input + rectangle as the first point relative to the matching corners of the + input rectangle. Coordinates are rounded to the nearest integer. */ + void unmap(int &x, int &y); + /** Maps a rectangle according to the inverse of the affine transform. This + operation consists in mapping the rectangle corners and reordering the + corners in the canonical rectangle representation. Variable #rect# is + overwritten with the new rectangle coordinates. */ + void unmap(GRect &rect); +private: + // GRatio + struct GRatio { + GRatio (); + GRatio (int p, int q); + int p; + int q; + }; + // Data + GRect rectFrom; + GRect rectTo; + int code; + // Helper + void precalc(); + friend int operator*(int n, GRatio r ); + friend int operator/(int n, GRatio r ); + GRatio rw; + GRatio rh; +}; + + +//@} + + + +// ---- INLINES + +inline +GRect::GRect() +: xmin(0), ymin(0), xmax(0), ymax(0) +{ +} + +inline +GRect::GRect(int xmin, int ymin, unsigned int width, unsigned int height) +: xmin(xmin), ymin(ymin), xmax(xmin+width), ymax(ymin+height) +{ +} + +inline int +GRect::width() const +{ + return xmax - xmin; +} + +inline int +GRect::height() const +{ + return ymax - ymin; +} + +inline int +GRect::isempty() const +{ + return (xmin>=xmax || ymin>=ymax); +} + +inline int +GRect::area() const +{ + return isempty() ? 0 : (xmax-xmin)*(ymax-ymin); +} + +inline int +GRect::contains(int x, int y) const +{ + return (x>=xmin && x<xmax && y>=ymin && y<ymax); +} + +inline void +GRect::clear() +{ + xmin = xmax = ymin = ymax = 0; +} + +inline int +operator!=(const GRect & r1, const GRect & r2) +{ + return !(r1==r2); +} + +// ---- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GScaler.cpp b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp new file mode 100644 index 00000000..0eeb9ebf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GScaler.cpp @@ -0,0 +1,706 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GScaler.cpp,v 1.11 2004/06/03 14:15:18 leonb Exp $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// Rescale images with fast bilinear interpolation +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GScaler.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +//////////////////////////////////////// +// CONSTANTS + + +#define FRACBITS 4 +#define FRACSIZE (1<<FRACBITS) +#define FRACSIZE2 (FRACSIZE>>1) +#define FRACMASK (FRACSIZE-1) + + + + + + +//////////////////////////////////////// +// UTILITIES + + +static int interp_ok = 0; +static short interp[FRACSIZE][512]; + +static void +prepare_interp() +{ + if (! interp_ok) + { + interp_ok = 1; + for (int i=0; i<FRACSIZE; i++) + { + short *deltas = & interp[i][256]; + for (int j = -255; j <= 255; j++) + deltas[j] = ( j*i + FRACSIZE2 ) >> FRACBITS; + } + } +} + + +static inline int +mini(int x, int y) +{ + return (x < y ? x : y); +} + + +static inline int +maxi(int x, int y) +{ + return (x > y ? x : y); +} + + + + + + +//////////////////////////////////////// +// GSCALER + + +GScaler::GScaler() + : inw(0), inh(0), + xshift(0), yshift(0), redw(0), redh(0), + outw(0), outh(0), + gvcoord(vcoord,0), ghcoord(hcoord,0) +{ +} + + +GScaler::~GScaler() +{ +} + + +void +GScaler::set_input_size(int w, int h) +{ + inw = w; + inh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +void +GScaler::set_output_size(int w, int h) +{ + outw = w; + outh = h; + if (vcoord) + { + gvcoord.resize(0); + } + if (hcoord) + { + ghcoord.resize(0); + } +} + + +static void +prepare_coord(int *coord, int inmax, int outmax, int in, int out) +{ + int len = (in*FRACSIZE); + int beg = (len+out)/(2*out) - FRACSIZE2; + // Bresenham algorithm + int y = beg; + int z = out/2; + int inmaxlim = (inmax-1)*FRACSIZE; + for (int x=0; x<outmax; x++) + { + coord[x] = mini(y,inmaxlim); + z = z + len; + y = y + z / out; + z = z % out; + } + // Result must fit exactly + if (out==outmax && y!=beg+len) + G_THROW( ERR_MSG("GScaler.assertion") ); +} + + +void +GScaler::set_horz_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outw; + denom = inw; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + xshift = 0; + redw = inw; + while (numer+numer < denom) { + xshift += 1; + redw = (redw + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! hcoord) + ghcoord.resize(outw); + prepare_coord(hcoord, redw, outw, denom, numer); +} + + +void +GScaler::set_vert_ratio(int numer, int denom) +{ + if (! (inw>0 && inh>0 && outw>0 && outh>0)) + G_THROW( ERR_MSG("GScaler.undef_size") ); + // Implicit ratio (determined by the input/output sizes) + if (numer==0 && denom==0) { + numer = outh; + denom = inh; + } else if (numer<=0 || denom<=0) + G_THROW( ERR_MSG("GScaler.ratios") ); + // Compute horz reduction + yshift = 0; + redh = inh; + while (numer+numer < denom) { + yshift += 1; + redh = (redh + 1) >> 1; + numer = numer << 1; + } + // Compute coordinate table + if (! vcoord) + { + gvcoord.resize(outh); + } + prepare_coord(vcoord, redh, outh, denom, numer); +} + + +void +GScaler::make_rectangles(const GRect &desired, GRect &red, GRect &inp) +{ + // Parameter validation + if (desired.xmin<0 || desired.ymin<0 || + desired.xmax>outw || desired.ymax>outh ) + G_THROW( ERR_MSG("GScaler.too_big") ); + // Compute ratio (if not done yet) + if (!vcoord) + set_vert_ratio(0,0); + if (!hcoord) + set_horz_ratio(0,0); + // Compute reduced bounds + red.xmin = (hcoord[desired.xmin]) >> FRACBITS; + red.ymin = (vcoord[desired.ymin]) >> FRACBITS; + red.xmax = (hcoord[desired.xmax-1]+FRACSIZE-1) >> FRACBITS; + red.ymax = (vcoord[desired.ymax-1]+FRACSIZE-1) >> FRACBITS; + // Borders + red.xmin = maxi(red.xmin, 0); + red.xmax = mini(red.xmax+1, redw); + red.ymin = maxi(red.ymin, 0); + red.ymax = mini(red.ymax+1, redh); + // Input + inp.xmin = maxi(red.xmin<<xshift, 0); + inp.xmax = mini(red.xmax<<xshift, inw); + inp.ymin = maxi(red.ymin<<yshift, 0); + inp.ymax = mini(red.ymax<<yshift, inh); +} + + +void +GScaler::get_input_rect( const GRect &desired_output, GRect &required_input ) +{ + GRect red; + make_rectangles(desired_output, red, required_input); +} + + + + + + +//////////////////////////////////////// +// GBITMAPSCALER + + +GBitmapScaler::GBitmapScaler() + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ +} + + +GBitmapScaler::GBitmapScaler(int inw, int inh, int outw, int outh) + : glbuffer(lbuffer,0), gconv(conv,0), gp1(p1,0), gp2(p2,0) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GBitmapScaler::~GBitmapScaler() +{ +} + + +unsigned char * +GBitmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GBitmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + unsigned char *p = p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + if (xshift==0 && yshift==0) + { + // Fast mode + int dx = required_red.xmin-provided_input.xmin; + int dx1 = required_red.xmax-provided_input.xmin; + const unsigned char *inp1 = input[fy-provided_input.ymin] + dx; + while (dx++ < dx1) + *p++ = conv[*inp1++]; + return p2; + } + else + { + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const unsigned char *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int g=0, s=0; + const unsigned char *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const unsigned char *inp1; + const unsigned char *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1=inp0; inp1<inp2; inp1++) + { + g += conv[*inp1]; + s += 1; + } + } + if (s == rnd+rnd) + *p = (g+rnd)>>div; + else + *p = (g+s/2)/s; + } + // Return + return p2; + } +} + + +void +GBitmapScaler::scale( const GRect &provided_input, const GBitmap &input, + const GRect &desired_output, GBitmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + output.set_grays(256); + // Prepare temp stuff + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2); + gp1.resize(bufw); + gp2.resize(bufw); + l1 = l2 = -1; + // Prepare gray conversion array (conv) + gconv.resize(0); + gconv.resize(256); + int maxgray = input.get_grays()-1; + for (int i=0; i<256; i++) + { + conv[i]=(i<= maxgray) + ?(((i*255) + (maxgray>>1)) / maxgray) + :255; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const unsigned char *lower, *upper; + // Obtain upper and lower line in reduced image + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + // Compute line + unsigned char *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(unsigned char const * const edest=(unsigned char const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int l = *lower; + const int u = *upper; + *dest = l + deltas[u-l]; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + unsigned char *line = lbuffer+1-required_red.xmin; + unsigned char *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++) + { + int n = hcoord[x]; + const unsigned char *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + int l = lower[0]; + int u = lower[1]; + *dest = l + deltas[u-l]; + dest++; + } + } + } + // Free temporaries + gp1.resize(0); + gp2.resize(0); + glbuffer.resize(0); + gconv.resize(0); +} + + + + + + +//////////////////////////////////////// +// GPIXMAPSCALER + + +GPixmapScaler::GPixmapScaler() + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ +} + + +GPixmapScaler::GPixmapScaler(int inw, int inh, int outw, int outh) + : glbuffer((void *&)lbuffer,0,sizeof(GPixel)), + gp1((void *&)p1,0,sizeof(GPixel)), + gp2((void *&)p2,0,sizeof(GPixel)) +{ + set_input_size(inw, inh); + set_output_size(outw, outh); +} + + +GPixmapScaler::~GPixmapScaler() +{ +} + + +GPixel * +GPixmapScaler::get_line(int fy, + const GRect &required_red, + const GRect &provided_input, + const GPixmap &input ) +{ + if (fy < required_red.ymin) + fy = required_red.ymin; + else if (fy >= required_red.ymax) + fy = required_red.ymax - 1; + // Cached line + if (fy == l2) + return p2; + if (fy == l1) + return p1; + // Shift + GPixel *p=p1; + p1 = p2; + l1 = l2; + p2 = p; + l2 = fy; + // Compute location of line + GRect line; + line.xmin = required_red.xmin << xshift; + line.xmax = required_red.xmax << xshift; + line.ymin = fy << yshift; + line.ymax = (fy+1) << yshift; + line.intersect(line, provided_input); + line.translate(-provided_input.xmin, -provided_input.ymin); + // Prepare variables + const GPixel *botline = input[line.ymin]; + int rowsize = input.rowsize(); + int sw = 1<<xshift; + int div = xshift+yshift; + int rnd = 1<<(div-1); + // Compute averages + for (int x=line.xmin; x<line.xmax; x+=sw,p++) + { + int r=0, g=0, b=0, s=0; + const GPixel *inp0 = botline + x; + int sy1 = mini(line.height(), (1<<yshift)); + for (int sy=0; sy<sy1; sy++,inp0+=rowsize) + { + const GPixel *inp1; + const GPixel *inp2 = inp0 + mini(x+sw, line.xmax) - x; + for (inp1 = inp0; inp1<inp2; inp1++) + { + r += inp1->r; + g += inp1->g; + b += inp1->b; + s += 1; + } + } + if (s == rnd+rnd) + { + p->r = (r+rnd) >> div; + p->g = (g+rnd) >> div; + p->b = (b+rnd) >> div; + } + else + { + p->r = (r+s/2)/s; + p->g = (g+s/2)/s; + p->b = (b+s/2)/s; + } + } + // Return + return (GPixel *)p2; +} + + +void +GPixmapScaler::scale( const GRect &provided_input, const GPixmap &input, + const GRect &desired_output, GPixmap &output ) +{ + // Compute rectangles + GRect required_input; + GRect required_red; + make_rectangles(desired_output, required_red, required_input); + // Parameter validation + if (provided_input.width() != (int)input.columns() || + provided_input.height() != (int)input.rows() ) + G_THROW( ERR_MSG("GScaler.no_match") ); + if (provided_input.xmin > required_input.xmin || + provided_input.ymin > required_input.ymin || + provided_input.xmax < required_input.xmax || + provided_input.ymax < required_input.ymax ) + G_THROW( ERR_MSG("GScaler.too_small") ); + // Adjust output pixmap + if (desired_output.width() != (int)output.columns() || + desired_output.height() != (int)output.rows() ) + output.init(desired_output.height(), desired_output.width()); + // Prepare temp stuff + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); + prepare_interp(); + const int bufw = required_red.width(); + glbuffer.resize(bufw+2,sizeof(GPixel)); + if (xshift>0 || yshift>0) + { + gp1.resize(bufw,sizeof(GPixel)); + gp2.resize(bufw,sizeof(GPixel)); + l1 = l2 = -1; + } + // Loop on output lines + for (int y=desired_output.ymin; y<desired_output.ymax; y++) + { + // Perform vertical interpolation + { + int fy = vcoord[y]; + int fy1 = fy>>FRACBITS; + int fy2 = fy1+1; + const GPixel *lower, *upper; + // Obtain upper and lower line in reduced image + if (xshift>0 || yshift>0) + { + lower = get_line(fy1, required_red, provided_input, input); + upper = get_line(fy2, required_red, provided_input, input); + } + else + { + int dx = required_red.xmin-provided_input.xmin; + fy1 = maxi(fy1, required_red.ymin); + fy2 = mini(fy2, required_red.ymax-1); + lower = input[fy1-provided_input.ymin] + dx; + upper = input[fy2-provided_input.ymin] + dx; + } + // Compute line + GPixel *dest = lbuffer+1; + const short *deltas = & interp[fy&FRACMASK][256]; + for(GPixel const * const edest = (GPixel const *)dest+bufw; + dest<edest;upper++,lower++,dest++) + { + const int lower_r = lower->r; + const int delta_r = deltas[(int)upper->r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower->g; + const int delta_g = deltas[(int)upper->g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower->b; + const int delta_b = deltas[(int)upper->b - lower_b]; + dest->b = lower_b + delta_b; + } + } + // Perform horizontal interpolation + { + // Prepare for side effects + lbuffer[0] = lbuffer[1]; + lbuffer[bufw] = lbuffer[bufw]; + GPixel *line = lbuffer+1-required_red.xmin; + GPixel *dest = output[y-desired_output.ymin]; + // Loop horizontally + for (int x=desired_output.xmin; x<desired_output.xmax; x++,dest++) + { + const int n = hcoord[x]; + const GPixel *lower = line + (n>>FRACBITS); + const short *deltas = &interp[n&FRACMASK][256]; + const int lower_r = lower[0].r; + const int delta_r = deltas[(int)lower[1].r - lower_r]; + dest->r = lower_r + delta_r; + const int lower_g = lower[0].g; + const int delta_g = deltas[(int)lower[1].g - lower_g]; + dest->g = lower_g + delta_g; + const int lower_b = lower[0].b; + const int delta_b = deltas[(int)lower[1].b - lower_b]; + dest->b = lower_b + delta_b; + } + } + } + // Free temporaries + gp1.resize(0,sizeof(GPixel)); + gp2.resize(0,sizeof(GPixel)); + glbuffer.resize(0,sizeof(GPixel)); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GScaler.h b/kviewshell/plugins/djvu/libdjvu/GScaler.h new file mode 100644 index 00000000..4843f6d0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GScaler.h @@ -0,0 +1,321 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GScaler.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSCALER_H_ +#define _GSCALER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GException.h" +#include "GRect.h" +#include "GBitmap.h" +#include "GPixmap.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name GScaler.h + + Files #"GScaler.h"# and #"GScaler.cpp"# implement a fast bilinear + interpolation scheme to rescale a \Ref{GBitmap} or a \Ref{GPixmap}. + Common setup functions are implemented by the base class \Ref{GScaler}. + The actual function for rescaling a gray level image is implemented by + class \Ref{GBitmapScaler}. The actual function for rescaling a color + image is implemented by class \Ref{GPixmapScaler}. + + {\bf Remark} --- The bilinear interpolation code relies on fixed precision + tables. It becomes suboptimal when upsampling (i.e. zooming into) an + image by a factor greater than eight. High contrast images displayed at + high magnification may contain visible jaggies. + + @memo + Rescaling images with bilinear interpolation. + @author + L\'eon Bottou <[email protected]> + @version + #$Id: GScaler.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +/** Base class for GBitmapScaler and GPixmapScaler. This base class + implements the common elements of class \Ref{GBitmapScaler} and + \Ref{GPixmapScaler}. Functions \Ref{set_input_size} and + \Ref{set_output_size} are used to specify the size of the input image and + the size of the output image. Functions \Ref{set_horz_ratio} and + \Ref{set_vert_ratio} may be used to override the scaling ratios computed + from the image sizes. You can then call function \Ref{get_input_rect} to + know which pixels of the input image are necessary to compute a specified + rectangular zone of the output image. The actual computation is then + performed by calling function #scale# in class \Ref{GBitmapScaler} and + \Ref{GPixmapScaler}. +*/ +class GScaler : public GPEnabled +{ +protected: + GScaler(); +public: + virtual ~GScaler(); + /** Sets the size of the input image. Argument #w# (resp. #h#) contains the + horizontal (resp. vertical) size of the input image. This size is used + to initialize the internal data structures of the scaler object. */ + void set_input_size(int w, int h); + /** Sets the size of the output image. Argument #w# (resp. #h#) contains the + horizontal (resp. vertical) size of the output image. This size is used + to initialize the internal data structures of the scaler object. */ + void set_output_size(int w, int h); + /** Sets the horizontal scaling ratio #numer/denom#. This function may be + used to force an exact scaling ratio. The scaling ratios are otherwise + derived from the sizes of the input and output images. */ + void set_horz_ratio(int numer, int denom); + /** Sets the vertical scaling ratio to #numer/denom#. This function may be + used to force an exact scaling ratio. The scaling ratios are otherwise + derived from the sizes of the input and output images. */ + void set_vert_ratio(int numer, int denom); + /** Computes which input pixels are required to compute specified output + pixels. Let us assume that we only need a part of the output + image. This part is defined by rectangle #desired_output#. Only a part + of the input image is necessary to compute the output pixels. Function + #get_input_rect# computes the coordinates of that part of the input + image, and stores them into rectangle #required_input#. */ + void get_input_rect( const GRect &desired_output, GRect &required_input ); +protected: + // The sizes + int inw, inh; + int xshift, yshift; + int redw, redh; + int outw, outh; + // Fixed point coordinates + int *vcoord; + GPBuffer<int> gvcoord; + int *hcoord; + GPBuffer<int> ghcoord; + // Helper + void make_rectangles(const GRect &desired, GRect &red, GRect &inp); +}; + + + +/** Fast rescaling code for gray level images. This class augments the base + class \Ref{GScaler} with a function for rescaling gray level + images. Function \Ref{GBitmapScaler::scale} computes an arbitrary segment + of the output image given the corresponding pixels in the input image. + + {\bf Example} --- The following functions returns an gray level image + (sixteen gray levels, size #nw# by #nh#) containing a rescaled version of + the input image #in#. + \begin{verbatim} + GBitmap *rescale_bitmap(const GBitmap &in, int nw, int nh) + { + int w = in.columns(); // Get input width + int h = in.raws(); // Get output width + GBitmapScaler scaler(w,h,nw,nh); // Creates bitmap scaler + GRect desired(0,0,nw,nh); // Desired output = complete bitmap + GRect provided(0,0,w,h); // Provided input = complete bitmap + GBitmap *out = new GBitmap; + scaler.scale(provided, in, desired, *out); // Rescale + out->change_grays(16); // Reduce to 16 gray levels + return out; + } + \end{verbatim} */ +class GBitmapScaler : public GScaler +{ +protected: + GBitmapScaler(void); + GBitmapScaler(int inw, int inh, int outw, int outh); +public: + /// Virtual destructor. + virtual ~GBitmapScaler(); + + /** Creates an empty GBitmapScaler. You must call functions + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size} before + calling any of the scaling functions. */ + static GP<GBitmapScaler> create(void) {return new GBitmapScaler(); } + + /** Creates a GBitmapScaler. The size of the input image is given by + #inw# and #inh#. This function internally calls + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size}. The + size of the output image is given by #outw# and #outh#. . */ + static GP<GBitmapScaler> create( + const int inw, const int inh, const int outw, const int outh) + { return new GBitmapScaler(inw,inh,outw,outh); } + + /** Computes a segment of the rescaled output image. The GBitmap object + #output# is overwritten with the segment of the output image specified + by the rectangle #desired_output#. The rectangle #provided_input# + specifies which segment of the input image is provided by the GBitmap + object #input#. An exception \Ref{GException} is thrown if the + rectangle #provided_input# is smaller then the rectangle + #required_input# returned by function \Ref{GScaler::get_input_rect}. + Note that the output image always contain 256 gray levels. You may want + to use function \Ref{GBitmap::change_grays} to reduce the number of gray + levels. */ + void scale( const GRect &provided_input, const GBitmap &input, + const GRect &desired_output, GBitmap &output ); +protected: + // Helpers + unsigned char *get_line(int, const GRect &, const GRect &, const GBitmap &); + // Temporaries + unsigned char *lbuffer; + GPBuffer<unsigned char> glbuffer; + unsigned char *conv; + GPBuffer<unsigned char> gconv; + unsigned char *p1; + GPBuffer<unsigned char> gp1; + unsigned char *p2; + GPBuffer<unsigned char> gp2; + int l1; + int l2; +}; + + +/** Fast rescaling code for color images. This class augments the base class + \Ref{GScaler} with a function for rescaling color images. Function + \Ref{GPixmapScaler::scale} computes an arbitrary segment of the output + image given the corresponding pixels in the input image. + + {\bf Example} --- The following functions returns a color image + of size #nw# by #nh# containing a rescaled version of + the input image #in#. + \begin{verbatim} + GPixmap *rescale_pixmap(const GPixmap &in, int nw, int nh) + { + int w = in.columns(); // Get input width + int h = in.raws(); // Get output width + GPixmapScaler scaler(w,h,nw,nh); // Creates bitmap scaler + GRect desired(0,0,nw,nh); // Desired output = complete image + GRect provided(0,0,w,h); // Provided input = complete image + GPixmap *out = new GPixmap; + scaler.scale(provided, in, desired, *out); // Rescale + return out; + } + \end{verbatim} + + */ +class GPixmapScaler : public GScaler +{ +protected: + GPixmapScaler(void); + GPixmapScaler(int inw, int inh, int outw, int outh); +public: + /// Virtual destructor. + virtual ~GPixmapScaler(); + + /** Creates an empty GPixmapScaler. You must call functions + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size} before + calling any of the scaling functions. */ + static GP<GPixmapScaler> create(void) {return new GPixmapScaler(); } + + /** Creates a GPixmapScaler. The size of the input image is given by + #inw# and #inh#. This function internally calls + \Ref{GScaler::set_input_size} and \Ref{GScaler::set_output_size}. The + size of the output image is given by #outw# and #outh#. . */ + static GP<GPixmapScaler> create( + const int inw, const int inh, const int outw, const int outh) + { return new GPixmapScaler(inw,inh,outw,outh); } + + /** Computes a segment of the rescaled output image. The pixmap #output# is + overwritten with the segment of the output image specified by the + rectangle #desired_output#. The rectangle #provided_input# specifies + which segment of the input image is provided in the pixmap #input#. An + exception \Ref{GException} is thrown if the rectangle #provided_input# + is smaller then the rectangle #required_input# returned by function + \Ref{GScaler::get_input_rect}. */ + void scale( const GRect &provided_input, const GPixmap &input, + const GRect &desired_output, GPixmap &output ); +protected: + // Helpers + GPixel *get_line(int, const GRect &, const GRect &, const GPixmap &); + // Temporaries + GPixel *lbuffer; + GPBufferBase glbuffer; + GPixel *p1; + GPBufferBase gp1; + GPixel *p2; + GPBufferBase gp2; + int l1; + int l2; +}; + + + + + +//@} + + + + +// -------- END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp new file mode 100644 index 00000000..8c17755d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp @@ -0,0 +1,256 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GSmartPointer.cpp,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 05/1997 + +// From: Leon Bottou, 1/31/2002 +// Class GPBuffer has been added (but not documented) by Lizardtech. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + +#include <string.h> +#include "GThreads.h" +#include "GSmartPointer.h" +#include "GException.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ------ STATIC CRITICAL SECTION + +static GCriticalSection gcsCounter; + + +// ------ GPENABLED + + +GPEnabled::~GPEnabled() +{ + if (count > 0) + G_THROW( ERR_MSG("GSmartPointer.suspicious") ); +} + +void +GPEnabled::destroy() +{ + if (count >= 0) + G_THROW( ERR_MSG("GSmartPointer.suspicious") ); + delete this; +} + +void +GPEnabled::ref() +{ + gcsCounter.lock(); + count++; + gcsCounter.unlock(); +} + +void +GPEnabled::unref() +{ + gcsCounter.lock(); + if (! --count) + count = -1; + gcsCounter.unlock(); + if (count < 0) + destroy(); +} + + +// ------ GPBASE + + +GPBase& +GPBase::assign (GPEnabled *nptr) +{ + gcsCounter.lock(); + if (nptr) + { + if (nptr->count >= 0) + nptr->count++; + else + nptr = 0; + } + if (ptr) + { + GPEnabled *old = ptr; + ptr = nptr; + if (! --old->count) + old->count = -1; + gcsCounter.unlock(); + if (old->count < 0) + old->destroy(); + } + else + { + ptr = nptr; + gcsCounter.unlock(); + } + return *this; +} + +GPBase& +GPBase::assign (const GPBase &sptr) +{ + gcsCounter.lock(); + if (sptr.ptr) + { + sptr.ptr->count++; + } + if (ptr) + { + GPEnabled *old = ptr; + ptr = sptr.ptr; + if (! --old->count) + old->count = -1; + gcsCounter.unlock(); + if (old->count < 0) + old->destroy(); + } + else + { + ptr = sptr.ptr; + gcsCounter.unlock(); + } + return *this; +} + + +// ------ GPBUFFERBASE + + +void +GPBufferBase::replace(void *nptr,const size_t n) +{ + resize(0,0); + ptr=nptr; + num=n; +} + +GPBufferBase::GPBufferBase(void *&xptr,const size_t n,const size_t t) + : ptr(xptr), num(n) +{ + if (n) + xptr = ::operator new(n*t); + else + xptr = 0; +} + +GPBufferBase::~GPBufferBase() +{ + ::operator delete(ptr); +} + +void +GPBufferBase::swap(GPBufferBase &other) +{ + void * const temp_ptr=ptr; + ptr=other.ptr; + other.ptr=temp_ptr; + const size_t temp_num=num; + num=other.num; + other.num=temp_num; +} + +void +GPBufferBase::resize(const size_t n, const size_t t) +{ + if(!n && !ptr) + { + num=0; + } + else + { + const size_t s=ptr?(((num<n)?num:n)*t):0; + void *nptr; + GPBufferBase gnptr(nptr, n, t); + if(s) + { + memcpy(nptr, ptr, s); + } + swap(gnptr); + } +} + +void +GPBufferBase::set(const size_t t,const char c) +{ + if(num) + memset(ptr,c,num*t); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h new file mode 100644 index 00000000..04b79ecf --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GSmartPointer.h @@ -0,0 +1,489 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GSmartPointer.h,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSMARTPOINTER_H_ +#define _GSMARTPOINTER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GSmartPointer.h + + Files #"GSmartPointer.h"# and #"GSmartPointer.cpp"# define a smart-pointer + class which automatically performs thread-safe reference counting. Class + \Ref{GP} implements smart-pointers by overloading the usual pointer + assignment and dereferencing operators. The overloaded operators maintain + the reference counters and destroy the pointed objects as soon as their + reference counter reaches zero. Transparent type conversions are provided + between smart-pointers and regular pointers. Objects referenced by + smart-pointers must be derived from class \Ref{GPEnabled}. + + @memo + Thread-Safe reference counting smart-pointers. + @author + L\'eon Bottou <[email protected]> -- initial implementation\\ + Andrei Erofeev <[email protected]> -- bug fix. + +// From: Leon Bottou, 1/31/2002 +// Class GPBuffer has been added (but not documented) by Lizardtech. +// Our original implementation consisted of multiple classes. +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. + + @version + #$Id: GSmartPointer.h,v 1.11 2003/11/07 22:08:21 leonb Exp $# + @args +*/ +//@{ + +#if defined(_MSC_VER) +// Language lawyer say MSVC6 is wrong on that one. +// Cf section 5.4.7 in november 1997 draft. +#pragma warning( disable : 4243 ) +#endif + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/* What is this innovation ? + What does it do that a GArray does not do ? */ + +class GPBufferBase +{ +public: + GPBufferBase(void *&,const size_t n,const size_t t); + void swap(GPBufferBase &p); + void resize(const size_t n,const size_t t); + void replace(void *nptr,const size_t n); + void set(const size_t t,const char c); + ~GPBufferBase(); + operator int(void) const { return ptr ? num : 0; } +private: + void *&ptr; + size_t num; +}; + +template<class TYPE> +class GPBuffer : public GPBufferBase +{ +public: + GPBuffer(TYPE *&xptr,const size_t n=0) : GPBufferBase((void *&)xptr,n,sizeof(TYPE)) {} + inline void resize(const size_t n) {GPBufferBase::resize(n,sizeof(TYPE));} + inline void clear(void) {GPBufferBase::set(sizeof(TYPE),0);} + inline void set(const char c) {GPBufferBase::set(sizeof(TYPE),c);} + inline operator int(void) const {return GPBufferBase::operator int();} +}; + +/** Base class for reference counted objects. + This is the base class for all reference counted objects. + Any instance of a subclass of #GPEnabled# can be used with + smart-pointers (see \Ref{GP}). + */ +class GPEnabled +{ +public: + /// Null constructor. + GPEnabled(); + /// Copy construcotr + GPEnabled(const GPEnabled & obj); + /// Virtual destructor. + virtual ~GPEnabled(); + /// Copy operator + GPEnabled & operator=(const GPEnabled & obj); + /** Returns the number of references to this object. This should be only + used for debugging purposes. Other uses are not thread-safe. */ + int get_count(void) const; +protected: + /// The reference counter + volatile int count; +private: + friend class GPBase; + void unref(); + void ref(); + void destroy(); +}; + + + +/** Base class for all smart-pointers. + This class implements common mechanisms for all + smart-pointers (see \Ref{GP}). There should be no need + to use this class directly. Its sole purpose consists + in reducing the template expansion overhead. +*/ + +class GPBase +{ +public: + /** Null Constructor. */ + GPBase(); + /** Copy Constructor. + Increments the reference count. + @param sptr reference to a #GPBase# object. */ + GPBase(const GPBase &sptr); + /** Construct a GPBase from a pointer. + Increments the reference count. + @param nptr pointer to a #GPEnabled# object. */ + GPBase(GPEnabled *nptr); + /** Destructor. Decrements the reference count. */ + ~GPBase(); + /** Accesses the actual pointer. */ + GPEnabled* get() const; + /** Assignment from smartpointer. + Increments the counter of the new value of the pointer. + Decrements the counter of the previous value of the pointer. */ + GPBase& assign(const GPBase &sptr); + /** Assignment from pointer. + Checks that the object is not being destroyed. + Increments the counter of the new value of the pointer. + Decrements the counter of the previous value of the pointer. */ + GPBase& assign(GPEnabled *nptr); + /** Assignment operator. */ + GPBase & operator=(const GPBase & obj); + /** Comparison operator. */ + int operator==(const GPBase & g2) const; +protected: + /** Actual pointer */ + GPEnabled *ptr; +}; + + +/** Reference counting pointer. + Class #GP<TYPE># represents a smart-pointer to an object of type #TYPE#. + Type #TYPE# must be a subclass of #GPEnabled#. This class overloads the + usual pointer assignment and dereferencing operators. The overloaded + operators maintain the reference counters and destroy the pointed object + as soon as their reference counter reaches zero. Transparent type + conversions are provided between smart-pointers and regular pointers. + + Using a smart-pointer is a convenience and not an obligation. There is no + need to use a smart-pointer to access a #GPEnabled# object. As long as + you never use a smart-pointer to access a #GPEnabled# object, its + reference counter remains zero. Since the reference counter is never + decremented from one to zero, the object is never destroyed by the + reference counting code. You can therefore choose to only use regular + pointers to access objects allocated on the stack (automatic variables) or + objects allocated dynamically. In the latter case you must explicitly + destroy the dynamically allocated object with operator #delete#. + + The first time you use a smart-pointer to access #GPEnabled# object, the + reference counter is incremented to one. Object destruction will then + happen automatically when the reference counter is decremented back to + zero (i.e. when the last smart-pointer referencing this object stops doing so). + This will happen regardless of how many regular pointers reference this object. + In other words, if you start using smart-pointers with a #GPEnabled# + object, you engage automatic mode for this object. You should only do + this with objects dynamically allocated with operator #new#. You should + never destroy the object yourself, but let the smart-pointers control the + life of the object. + + {\bf Performance considerations} --- Thread safe reference counting incurs + a significant overhead. Smart-pointer are best used with sizeable objects + for which the cost of maintaining the counters represent a small fraction + of the processing time. It is always possible to cache a smart-pointer + into a regular pointer. The cached pointer will remain valid until the + smart-pointer object is destroyed or the smart-pointer value is changed. + + {\bf Safety considerations} --- As explained above, a #GPEnabled# object + switches to automatic mode as soon as it becomes referenced by a + smart-pointer. There is no way to switch the object back to manual mode. + Suppose that you have decided to only use regular pointers with a + particular #GPEnabled# object. You therefore plan to destroy the object + explicitly when you no longer need it. When you pass a regular pointer to + this object as argument to a function, you really need to be certain that + the function implementation will not assign this pointer to a + smart-pointer. Doing so would indeed destroy the object as soon as the + function returns. The bad news is that the fact that a function assigns a + pointer argument to a smart-pointer does not necessarily appear in the + function prototype. Such a behavior must be {\em documented} with the + function public interface. As a convention, we usually write such + functions with smart-pointer arguments instead of a regular pointer + arguments. This is not enough to catch the error at compile time, but + this is a simple way to document such a behavior. We still believe that + this is a small problem in regard to the benefits of the smart-pointer. + But one has to be aware of its existence. */ + +template <class TYPE> +class GP : protected GPBase +{ +public: + /** Constructs a null smart-pointer. */ + GP(); + /** Constructs a copy of a smart-pointer. + @param sptr smart-pointer to copy. */ + GP(const GP<TYPE> &sptr); + /** Constructs a smart-pointer from a regular pointer. + The pointed object must be dynamically allocated (with operator #new#). + You should no longer explicitly destroy the object referenced by #sptr# + since the object life is now controlled by smart-pointers. + @param nptr regular pointer to a {\em dynamically allocated object}. */ + GP(TYPE *nptr); + /** Converts a smart-pointer into a regular pointer. + This is useful for caching the value of a smart-pointer for performances + purposes. The cached pointer will remain valid until the smart-pointer + is destroyed or until the smart-pointer value is changed. */ + operator TYPE* () const; + /** Assigns a regular pointer to a smart-pointer lvalue. + The pointed object must be dynamically allocated (with operator #new#). + You should no longer explicitly destroy the object referenced by #sptr# + since the object life is now controlled by smart-pointers. + @param nptr regular pointer to a {\em dynamically allocated object}. */ + GP<TYPE>& operator= (TYPE *nptr); + /** Assigns a smart-pointer to a smart-pointer lvalue. + @param sptr smart-pointer copied into this smart-pointer. */ + GP<TYPE>& operator= (const GP<TYPE> &sptr); + /** Indirection operator. + This operator provides a convenient access to the members + of a smart-pointed object. Operator #-># works with smart-pointers + exactly as with regular pointers. */ + TYPE* operator->() const; + /** Dereferencement operator. + This operator provides a convenient access to the smart-pointed object. + Operator #*# works with smart-pointers exactly as with regular pointers. */ + TYPE& operator*() const; + /** Comparison operator. + Returns true if both this smart-pointer and pointer #nptr# point to the + same object. The automatic conversion from smart-pointers to regular + pointers allows you to compare two smart-pointers as well. + @param nptr pointer to compare with. */ + int operator== (TYPE *nptr) const; + /** Comparison operator. + Returns true if this smart-pointer and pointer #nptr# point to different + objects. The automatic conversion from smart-pointers to regular + pointers allows you to compare two smart-pointers as well. + @param nptr pointer to compare with. */ + int operator!= (TYPE *nptr) const; + /** Test operator. + Returns true if the smart-pointer is null. The automatic conversion + from smart-pointers to regular pointers allows you to test whether + a smart-pointer is non-null. You can use both following constructs: + \begin{verbatim} + if (gp) { ... } + while (! gp) { ... } + \end{verbatim} */ + int operator! () const; +}; + +//@} + +// INLINE FOR GPENABLED + +inline +GPEnabled::GPEnabled() + : count(0) +{ +} + +inline +GPEnabled::GPEnabled(const GPEnabled & obj) + : count(0) +{ + +} + +inline int +GPEnabled::get_count(void) const +{ + return count; +} + +inline GPEnabled & +GPEnabled::operator=(const GPEnabled & obj) +{ + /* The copy operator should do nothing because the count should not be + changed. Subclasses of GPEnabled will call this version of the copy + operator as part of the default 'memberwise copy' strategy. */ + return *this; +} + +// INLINE FOR GPBASE + +inline +GPBase::GPBase() + : ptr(0) +{ +} + +inline +GPBase::GPBase(GPEnabled *nptr) + : ptr(0) +{ + assign(nptr); +} + +inline +GPBase::GPBase(const GPBase &sptr) +{ + if (sptr.ptr) + sptr.ptr->ref(); + ptr = sptr.ptr; +} + +inline +GPBase::~GPBase() +{ + GPEnabled *old = ptr; + ptr = 0; + if (old) + old->unref(); +} + +inline GPEnabled* +GPBase::get() const +{ + return ptr; +} + +inline GPBase & +GPBase::operator=(const GPBase & obj) +{ + return assign(obj); +} + +inline int +GPBase::operator==(const GPBase & g2) const +{ + return ptr == g2.ptr; +} + + + + +// INLINE FOR GP<TYPE> + +template <class TYPE> inline +GP<TYPE>::GP() +{ +} + +template <class TYPE> inline +GP<TYPE>::GP(TYPE *nptr) +: GPBase((GPEnabled*)nptr) +{ +} + +template <class TYPE> inline +GP<TYPE>::GP(const GP<TYPE> &sptr) +: GPBase((const GPBase&) sptr) +{ +} + +template <class TYPE> inline +GP<TYPE>::operator TYPE* () const +{ + return (TYPE*) ptr; +} + +template <class TYPE> inline TYPE* +GP<TYPE>::operator->() const +{ + return (TYPE*) ptr; +} + +template <class TYPE> inline TYPE& +GP<TYPE>::operator*() const +{ + return *(TYPE*) ptr; +} + +template <class TYPE> inline GP<TYPE>& +GP<TYPE>::operator= (TYPE *nptr) +{ + return (GP<TYPE>&)( assign(nptr) ); +} + +template <class TYPE> inline GP<TYPE>& +GP<TYPE>::operator= (const GP<TYPE> &sptr) +{ + return (GP<TYPE>&)( assign((const GPBase&)sptr) ); +} + +template <class TYPE> inline int +GP<TYPE>::operator== (TYPE *nptr) const +{ + return ( (TYPE*)ptr == nptr ); +} + +template <class TYPE> inline int +GP<TYPE>::operator!= (TYPE *nptr) const +{ + return ( (TYPE*)ptr != nptr ); +} + +template <class TYPE> inline int +GP<TYPE>::operator! () const +{ + return !ptr; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GString.cpp b/kviewshell/plugins/djvu/libdjvu/GString.cpp new file mode 100644 index 00000000..a618055e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GString.cpp @@ -0,0 +1,2811 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GString.cpp,v 1.22 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// My original implementation was very small in comparison +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. +// In my opinion, the duplication of the string classes is a failed +// attempt to use the type system to enforce coding policies. +// This could be fixed. But there are better things to do in djvulibre. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GString.h" +#include "GThreads.h" +#include "debug.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if HAS_WCHAR +# include <locale.h> +# if !defined(AUTOCONF) || HAVE_WCHAR_H +# include <wchar.h> +# endif +# if HAS_WCTYPE +# include <wctype.h> +# endif +#endif +#include <ctype.h> + +#ifndef DO_CHANGELOCALE +#define DO_CHANGELOCALE 1 +#ifdef UNIX +#if THREADMODEL != COTHREADS +#if THREADMODEL != NOTHREADS +#undef DO_CHANGELOCALE +#define DO_CHANGELOCALE 0 +#endif +#endif +#endif +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +GBaseString::~GBaseString() {} +GNativeString::~GNativeString() {} +GUTF8String::~GUTF8String() {} + +#if !HAS_MBSTATE && HAS_WCHAR +// Under some systems, wctomb() and mbtowc() are not thread +// safe. In those cases, wcrtomb and mbrtowc are preferred. +// For Solaris, wctomb() and mbtowc() are thread safe, and +// wcrtomb() and mbrtowc() don't exist. + +#define wcrtomb MYwcrtomb +#define mbrtowc MYmbrtowc +#define mbrlen MYmbrlen + +static inline int +wcrtomb(char *bytes,wchar_t w,mbstate_t *) +{ + return wctomb(bytes,w); +} + +static inline int +mbrtowc(wchar_t *w,const char *source, size_t n, mbstate_t *) +{ + return mbtowc(w,source,n); +} + +static inline size_t +mbrlen(const char *s, size_t n, mbstate_t *) +{ + return mblen(s,n); +} +#endif // !HAS_MBSTATE || HAS_WCHAR + + +GP<GStringRep> +GStringRep::upcase(void) const +{ return tocase(giswupper,gtowupper); } + +GP<GStringRep> +GStringRep::downcase(void) const +{ return tocase(giswlower,gtowlower); } + +GP<GStringRep> +GStringRep::UTF8::create(const unsigned int sz) +{ + return GStringRep::create(sz,(GStringRep::UTF8 *)0); +} + +GP<GStringRep> +GStringRep::UTF8::create(const char *s) +{ + GStringRep::UTF8 dummy; + return dummy.strdup(s); +} + +GP<GStringRep> +GStringRep::UTF8::create(const GP<GStringRep> &s1,const GP<GStringRep> &s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const GP<GStringRep> &s1,const char *s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const char *s1, const GP<GStringRep> &s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create( const char *s1,const char *s2) +{ + GStringRep::UTF8 dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::UTF8::create(const char *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::create( + const unsigned short *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::create( + const unsigned long *s,const int start,const int length) +{ + GStringRep::UTF8 dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::UTF8::blank(const unsigned int sz) const +{ + return GStringRep::create(sz,(GStringRep::UTF8 *)0); +} + +bool +GStringRep::UTF8::isUTF8(void) const +{ + return true; +} + +GP<GStringRep> +GStringRep::UTF8::toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const +{ + return rep?(rep->toUTF8(true)):rep; +} + +GP<GStringRep> +GStringRep::UTF8::create(const char fmt[],va_list& args) +{ + const GP<GStringRep> s(create(fmt)); + return (s?(s->vformat(args)):s); +} + +#if !HAS_WCHAR + +#define NATIVE_CREATE(x) UTF8::create( x ); + +#ifdef LC_ALL +#undef LC_ALL +#endif +#define LC_ALL 0 + +class GStringRep::ChangeLocale +{ +public: + ChangeLocale(const int,const char *) {} + ~ChangeLocale() {}; +}; + +GP<GStringRep> +GStringRep::NativeToUTF8( const char *s ) +{ + return GStringRep::UTF8::create(s); +} + +#else + +#define NATIVE_CREATE(x) Native::create( x ); + +// The declaration and implementation of GStringRep::ChangeLocale +// Not used in WinCE + +class GStringRep::ChangeLocale +{ +public: + ChangeLocale(const int category,const char locale[]); + ~ChangeLocale(); +private: + GUTF8String locale; + int category; +}; + +class GStringRep::Native : public GStringRep +{ +public: + // default constructor + Native(void); + // virtual destructor + virtual ~Native(); + + // Other virtual methods. + // Create an empty string. + virtual GP<GStringRep> blank(const unsigned int sz = 0) const; + // Append a string. + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const; + // Test if Native. + virtual bool isNative(void) const; + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED) const; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const; + // Convert to UTF8. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2, const int len=(-1)) const; + + // Convert strings to numbers. + virtual int toInt(void) const; + virtual long toLong( + const int pos, int &endpos, const int base=10) const; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + virtual double toDouble( + const int pos, int &endpos) const; + + // Create an empty string + static GP<GStringRep> create(const unsigned int sz = 0); + + // Create a strdup string. + static GP<GStringRep> create(const char *s); + + // Creates by appending to the current string + + // Creates with a concat operation. + static GP<GStringRep> create( + const GP<GStringRep> &s1,const GP<GStringRep> &s2); + static GP<GStringRep> create( const GP<GStringRep> &s1,const char *s2); + static GP<GStringRep> create( const char *s1, const GP<GStringRep> &s2); + static GP<GStringRep> create(const char *s1,const char *s2); + + // Create with a strdup and substr operation. + static GP<GStringRep> create( + const char *s,const int start,const int length=(-1)); + static GP<GStringRep> create( + const unsigned short *s,const int start,const int length=(-1)); + static GP<GStringRep> create( + const unsigned long *s,const int start,const int length=(-1)); + + // Create with an sprintf() + static GP<GStringRep> create_format(const char fmt[],...); + static GP<GStringRep> create(const char fmt[],va_list &args); + + virtual unsigned char *UCS4toString( + const unsigned long w,unsigned char *ptr, mbstate_t *ps=0) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const; + + virtual int ncopy(wchar_t * const buf, const int buflen) const; + + friend class GBaseString; +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const; +}; + +GP<GStringRep> +GStringRep::Native::create(const unsigned int sz) +{ + return GStringRep::create(sz,(GStringRep::Native *)0); +} + + // Create a strdup string. +GP<GStringRep> +GStringRep::Native::create(const char *s) +{ + GStringRep::Native dummy; + return dummy.strdup(s); +} + +GP<GStringRep> +GStringRep::Native::create(const GP<GStringRep> &s1,const GP<GStringRep> &s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( const GP<GStringRep> &s1,const char *s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( const char *s1, const GP<GStringRep> &s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create(const char *s1,const char *s2) +{ + GStringRep::Native dummy; + return dummy.concat(s1,s2); +} + +GP<GStringRep> +GStringRep::Native::create( + const char *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::create( + const unsigned short *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::create( + const unsigned long *s,const int start,const int length) +{ + GStringRep::Native dummy; + return dummy.substr(s,start,length); +} + +GP<GStringRep> +GStringRep::Native::blank(const unsigned int sz) const +{ + return GStringRep::create(sz,(GStringRep::Native *)0); +} + +bool +GStringRep::Native::isNative(void) const +{ + return true; +} + +GP<GStringRep> +GStringRep::Native::toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const +{ + return rep?(rep->toNative(NOT_ESCAPED)):rep; +} + +GP<GStringRep> +GStringRep::Native::create(const char fmt[],va_list &args) +{ + const GP<GStringRep> s(create(fmt)); + return (s?(s->vformat(args)):s); +} + +int +GStringRep::Native::ncopy( + wchar_t * const buf, const int buflen ) const +{ + return toUTF8()->ncopy(buf,buflen); +} + +GStringRep::ChangeLocale::ChangeLocale(const int xcategory, const char xlocale[] ) + : category(xcategory) +{ +#if DO_CHANGELOCALE + // This is disabled under UNIX because + // it does not play nice with MT. + if(xlocale) + { + locale=setlocale(xcategory,0); + if(locale.length() &&(locale!=xlocale)) + { + if(locale == setlocale(category,xlocale)) + { + locale.empty(); + } + } + else + { + locale.empty(); + } + } +#endif +} + +GStringRep::ChangeLocale::~ChangeLocale() +{ +#if DO_CHANGELOCALE + if(locale.length()) + { + setlocale(category,(const char *)locale); + } +#endif +} + +GNativeString & +GNativeString::format(const char fmt[], ... ) +{ + va_list args; + va_start(args, fmt); + return init(GStringRep::Native::create(fmt,args)); +} + +// Gather the native implementations here. Not used in WinCE. + +GStringRep::Native::Native(void) {} +GStringRep::Native::~Native() {} + +GP<GStringRep> +GStringRep::Native::append(const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + if(s2->isUTF8()) + { + G_THROW( ERR_MSG("GStringRep.appendUTF8toNative") ); + } + retval=concat(data,s2->data); + }else + { + retval=const_cast<GStringRep::Native *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::Native::create_format(const char fmt[],...) +{ + va_list args; + va_start(args, fmt); + return create(fmt,args); +} + +unsigned char * +GStringRep::Native::UCS4toString( + const unsigned long w0,unsigned char *ptr, mbstate_t *ps) const +{ + return UCS4toNative(w0,ptr,ps); +} + +// Convert a UCS4 to a multibyte string in the value bytes. +// The data pointed to by ptr should be long enough to contain +// the results with a nill termination. (Normally 7 characters +// is enough.) +unsigned char * +GStringRep::UCS4toNative( + const unsigned long w0,unsigned char *ptr, mbstate_t *ps) +{ + unsigned short w1; + unsigned short w2=1; + for(int count=(sizeof(wchar_t)==sizeof(w1)) ? UCS4toUTF16(w0,w1,w2) : 1; + count; + --count,w1=w2) + { + // wchar_t can be either UCS4 or UCS2 + const wchar_t w=(sizeof(wchar_t) == sizeof(w1))?(wchar_t)w1:(wchar_t)w0; + int i=wcrtomb((char *)ptr,w,ps); + if(i<0) + { + break; + } + ptr[i]=0; + ptr += i; + } + ptr[0]=0; + return ptr; +} + +GP<GStringRep> +GStringRep::Native::toNative(const EscapeMode escape) const +{ + if(escape == UNKNOWN_ESCAPED) + G_THROW( ERR_MSG("GStringRep.NativeToNative") ); + return const_cast<GStringRep::Native *>(this); +} + +GP<GStringRep> +GStringRep::Native::toUTF8(const bool) const +{ + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,size*6+1); + buf[0]=0; + if(data && size) + { + size_t n=size; + const char *source=data; + mbstate_t ps; + unsigned char *ptr=buf; + //(void)mbrlen(source, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + int i=0; + if(sizeof(wchar_t) == sizeof(unsigned long)) + { + wchar_t w = 0; + for(;(n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0); n-=i,source+=i) + { + ptr=UCS4toUTF8(w,ptr); + } + } + else + { + wchar_t w = 0; + for(;(n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0);n-=i,source+=i) + { + unsigned short s[2]; + s[0]=w; + unsigned long w0; + if(UTF16toUCS4(w0,s,s+1)<=0) + { + source+=i; + n-=i; + if((n>0)&&((i=mbrtowc(&w,source,n,&ps))>=0)) + { + s[1]=w; + if(UTF16toUCS4(w0,s,s+2)<=0) + { + i=(-1); + break; + } + } + else + { + i=(-1); + break; + } + } + ptr=UCS4toUTF8(w0,ptr); + } + } + if(i<0) + { + gbuf.resize(0); + } + else + { + ptr[0]=0; + } + } + return GStringRep::UTF8::create((const char *)buf); +} + +GNativeString +GBaseString::UTF8ToNative( + const bool currentlocale,const EscapeMode escape) const +{ + const char *source=(*this); + GP<GStringRep> retval; + if(source && source[0]) + { +#if DO_CHANGELOCALE + GUTF8String lc_ctype(setlocale(LC_CTYPE,0)); +#endif + bool repeat; + for(repeat=!currentlocale;;repeat=false) + { + retval=(*this)->toNative((GStringRep::EscapeMode)escape); +#if DO_CHANGELOCALE + if (!repeat || retval || (lc_ctype == setlocale(LC_CTYPE,""))) +#endif + break; + } +#if DO_CHANGELOCALE + if(!repeat) + { + setlocale(LC_CTYPE,(const char *)lc_ctype); + } +#endif + } + return GNativeString(retval); +} + +/*MBCS*/ +GNativeString +GBaseString::getUTF82Native( const EscapeMode escape ) const +{ //MBCS cvt + GNativeString retval; + + // We don't want to convert this if it + // already is known to be native... +// if (isNative()) return *this; + + const size_t slen=length()+1; + if(slen>1) + { + retval=UTF8ToNative(false,escape) ; + if(!retval.length()) + { + retval=(const char*)*this; + } + } + return retval; +} + +GUTF8String +GBaseString::NativeToUTF8(void) const +{ + GP<GStringRep> retval; + if(length()) + { + const char *source=(*this); +#if DO_CHANGELOCALE + GUTF8String lc_ctype=setlocale(LC_CTYPE,0); +#endif + bool repeat; + for(repeat=true;;repeat=false) + { + if( (retval=GStringRep::NativeToUTF8(source)) ) + { + if(GStringRep::cmp(retval->toNative(),source)) + { + retval=GStringRep::UTF8::create((unsigned int)0); + } + } +#if DO_CHANGELOCALE + if(!repeat || retval || (lc_ctype == setlocale(LC_CTYPE,""))) +#endif + break; + } +#if DO_CHANGELOCALE + if(!repeat) + { + setlocale(LC_CTYPE,(const char *)lc_ctype); + } +#endif + } + return GUTF8String(retval); +} + +GUTF8String +GBaseString::getNative2UTF8(void) const +{ //MBCS cvt + + // We don't want to do a transform this + // if we already are in the given type. +// if (isUTF8()) return *this; + + const size_t slen=length()+1; + GUTF8String retval; + if(slen > 1) + { + retval=NativeToUTF8(); + if(!retval.length()) + { + retval=(const char *)(*this); + } + } + return retval; +} /*MBCS*/ + +int +GStringRep::Native::cmp(const GP<GStringRep> &s2,const int len) const +{ + int retval; + if(s2) + { + if(s2->isUTF8()) + { + const GP<GStringRep> r(toUTF8(true)); + if(r) + { + retval=GStringRep::cmp(r->data,s2->data,len); + }else + { + retval=cmp(s2->toNative(NOT_ESCAPED),len); + } + }else + { + retval=GStringRep::cmp(data,s2->data,len); + } + }else + { + retval=GStringRep::cmp(data,0,len); + } + return retval; +} + +int +GStringRep::Native::toInt() const +{ + return atoi(data); +} + +long +GStringRep::Native::toLong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + const long retval=strtol(data+pos, &edata, base); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +unsigned long +GStringRep::Native::toULong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + const unsigned long retval=strtoul(data+pos, &edata, base); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +double +GStringRep::Native::toDouble( + const int pos, int &endpos) const +{ + char *edata=0; + const double retval=strtod(data+pos, &edata); + if(edata) + { + endpos=(int)((size_t)edata-(size_t)data); + }else + { + endpos=(-1); + } + return retval; +} + +unsigned long +GStringRep::Native::getValidUCS4(const char *&source) const +{ + unsigned long retval=0; + int n=(int)((size_t)size+(size_t)data-(size_t)source); + if(source && (n > 0)) + { + mbstate_t ps; + //(void)mbrlen(source, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + wchar_t wt; + const int len=mbrtowc(&wt,source,n,&ps); + if(len>=0) + { + if(sizeof(wchar_t) == sizeof(unsigned short)) + { + source+=len; + unsigned short s[2]; + s[0]=(unsigned short)wt; + if(UTF16toUCS4(retval,s,s+1)<=0) + { + if((n-=len)>0) + { + const int len=mbrtowc(&wt,source,n,&ps); + if(len>=0) + { + s[1]=(unsigned short)wt; + unsigned long w; + if(UTF16toUCS4(w,s,s+2)>0) + { + source+=len; + retval=w; + } + } + } + } + }else + { + retval=(unsigned long)wt; + source++; + } + }else + { + source++; + } + } + return retval; +} + +// Tests if a string is legally encoded in the current character set. +bool +GStringRep::Native::is_valid(void) const +{ + bool retval=true; + if(data && size) + { + size_t n=size; + const char *s=data; + mbstate_t ps; + //(void)mbrlen(s, n, &ps); + memset(&ps,0,sizeof(mbstate_t)); + do + { + size_t m=mbrlen(s,n,&ps); + if(m > n) + { + retval=false; + break; + }else if(m) + { + s+=m; + n-=m; + }else + { + break; + } + } while(n); + } + return retval; +} + +// These are dummy functions. +void +GStringRep::set_remainder(void const * const, const unsigned int, + const EncodeType) {} +void +GStringRep::set_remainder(void const * const, const unsigned int, + const GP<GStringRep> &encoding) {} +void +GStringRep::set_remainder( const GP<GStringRep::Unicode> &) {} + +GP<GStringRep::Unicode> +GStringRep::get_remainder( void ) const +{ + return 0; +} + +GNativeString::GNativeString(const char dat) +{ + init(GStringRep::Native::create(&dat,0,1)); +} + +GNativeString::GNativeString(const char *str) +{ + init(GStringRep::Native::create(str)); +} + +GNativeString::GNativeString(const unsigned char *str) +{ + init(GStringRep::Native::create((const char *)str)); +} + +GNativeString::GNativeString(const unsigned short *str) +{ + init(GStringRep::Native::create(str,0,-1)); +} + +GNativeString::GNativeString(const unsigned long *str) +{ + init(GStringRep::Native::create(str,0,-1)); +} + +GNativeString::GNativeString(const char *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const unsigned short *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const unsigned long *dat, unsigned int len) +{ + init( + GStringRep::Native::create(dat,0,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const GNativeString &str) +{ + init(str); +} + +GNativeString::GNativeString(const GBaseString &gs, int from, int len) +{ + init( + GStringRep::Native::create(gs,from,((int)len<0)?(-1):(int)len)); +} + +GNativeString::GNativeString(const int number) +{ + init(GStringRep::Native::create_format("%d",number)); +} + +GNativeString::GNativeString(const double number) +{ + init(GStringRep::Native::create_format("%f",number)); +} + +GNativeString& +GNativeString::operator= (const char str) +{ return init(GStringRep::Native::create(&str,0,1)); } + +GNativeString& +GNativeString::operator= (const char *str) +{ return init(GStringRep::Native::create(str)); } + +GNativeString +GBaseString::operator+(const GNativeString &s2) const +{ + return GStringRep::Native::create(*this,s2); +} + +GP<GStringRep> +GStringRep::NativeToUTF8( const char *s ) +{ + return GStringRep::Native::create(s)->toUTF8(); +} + +#endif // HAS_WCHAR + +template <class TYPE> +GP<GStringRep> +GStringRep::create(const unsigned int sz, TYPE *) +{ + GP<GStringRep> gaddr; + if (sz > 0) + { + GStringRep *addr; + gaddr=(addr=new TYPE); + addr->data=(char *)(::operator new(sz+1)); + addr->size = sz; + addr->data[sz] = 0; + } + return gaddr; +} + +GP<GStringRep> +GStringRep::strdup(const char *s) const +{ + GP<GStringRep> retval; + const int length=s?strlen(s):0; + if(length>0) + { + retval=blank(length); + char const * const end=s+length; + char *ptr=retval->data; + for(;*s&&(s!=end);ptr++) + { + ptr[0]=s++[0]; + } + ptr[0]=0; + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const char *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + const unsigned int length=(start<0 || len<0)?(unsigned int)strlen(s):(unsigned int)(-1); + const char *startptr, *endptr; + if(start<0) + { + startptr=s+length+start; + if(startptr<s) + startptr=s; + }else + { + startptr=s; + for(const char * const ptr=s+start;(startptr<ptr)&&*startptr;++startptr) + EMPTY_LOOP; + } + if(len<0) + { + if(s+length+1 < startptr+len) + { + endptr=startptr; + }else + { + endptr=s+length+1+len; + } + }else + { + endptr=startptr; + for(const char * const ptr=startptr+len;(endptr<ptr)&&*endptr;++endptr) + EMPTY_LOOP; + } + if(endptr>startptr) + { + retval=blank((size_t)(endptr-startptr)); + char *data=retval->data; + for(; (startptr<endptr) && *startptr; ++startptr,++data) + { + data[0]=startptr[0]; + } + data[0]=0; + } + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const unsigned short *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + unsigned short const *eptr; + if(len<0) + { + for(eptr=s;eptr[0];++eptr) + EMPTY_LOOP; + }else + { + eptr=&(s[len]); + } + s=&s[start]; + if((size_t)s<(size_t)eptr) + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + unsigned char *buf,*ptr; + GPBuffer<unsigned char> gbuf(buf,(((size_t)eptr-(size_t)s)/2)*3+7); + for(ptr=buf;s[0];) + { + unsigned long w; + int i=UTF16toUCS4(w,s,eptr); + if(i<=0) + break; + s+=i; + ptr=UCS4toString(w,ptr,&ps); + } + ptr[0]=0; + retval = strdup( (const char *)buf ); + } + } + return retval; +} + +GP<GStringRep> +GStringRep::substr(const unsigned long *s,const int start,const int len) const +{ + GP<GStringRep> retval; + if(s && s[0]) + { + unsigned long const *eptr; + if(len<0) + { + for(eptr=s;eptr[0];++eptr) + EMPTY_LOOP; + }else + { + eptr=&(s[len]); + } + s=&s[start]; + if((size_t)s<(size_t)eptr) + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + unsigned char *buf,*ptr; + GPBuffer<unsigned char> gbuf(buf,((((size_t)eptr-(size_t)s))/4)*6+7); + for(ptr=buf;s[0];++s) + { + ptr=UCS4toString(s[0],ptr,&ps); + } + ptr[0]=0; + retval = strdup( (const char *)buf ); + } + } + return retval; +} + +GP<GStringRep> +GStringRep::append(const char *s2) const +{ + GP<GStringRep> retval; + if(s2) + { + retval=concat(data,s2); + }else + { + retval=const_cast<GStringRep *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::append(const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + if(s2->isNative()) + { + G_THROW( ERR_MSG("GStringRep.appendNativeToUTF8") ); + } + retval=concat(data,s2->data); + }else + { + retval=const_cast<GStringRep::UTF8 *>(this); + } + return retval; +} + +GP<GStringRep> +GStringRep::concat(const char *s1,const char *s2) const +{ + const int length1=(s1?strlen(s1):0); + const int length2=(s2?strlen(s2):0); + const int length=length1+length2; + GP<GStringRep> retval; + if(length>0) + { + retval=blank(length); + GStringRep &r=*retval; + if(length1) + { + strcpy(r.data,s1); + if(length2) + strcat(r.data,s2); + }else + { + strcpy(r.data,s2); + } + } + return retval; +} + +const char *GBaseString::nullstr = ""; + +void +GBaseString::empty( void ) +{ + init(0); +} + +GP<GStringRep> +GStringRep::getbuf(int n) const +{ + GP<GStringRep> retval; + if(n< 0) + n=strlen(data); + if(n>0) + { + retval=blank(n); + char *ndata=retval->data; + strncpy(ndata,data,n); + ndata[n]=0; + } + return retval; +} + +const char * +GStringRep::isCharType( + bool (*xiswtest)(const unsigned long wc), const char *ptr, const bool reverse) const +{ + char const * xptr=ptr; + const unsigned long w=getValidUCS4(xptr); + if((ptr != xptr) + &&(((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ||(reverse?(!xiswtest(w)):xiswtest(w)))) + { + ptr=xptr; + } + return ptr; +} + +int +GStringRep::nextCharType( + bool (*xiswtest)(const unsigned long wc), const int from, const int len, + const bool reverse) const +{ + // We want to return the position of the next + // non white space starting from the #from# + // location. isspace should work in any locale + // so we should only need to do this for the non- + // native locales (UTF8) + int retval; + if(from<size) + { + retval=from; + const char * ptr = data+from; + for( const char * const eptr=ptr+((len<0)?(size-from):len); + (ptr<eptr) && *ptr;) + { + // Skip characters that fail the isCharType test + char const * const xptr=isCharType(xiswtest,ptr,!reverse); + if(xptr == ptr) + break; + ptr=xptr; + } + retval=(int)((size_t)ptr-(size_t)data); + }else + { + retval=size; + } + return retval; +} + +bool +GStringRep::giswspace(const unsigned long w) +{ +#if HAS_WCTYPE + return + ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ||((unsigned long)iswspace((wchar_t)w)) + ||((w == '\r')||(w == '\n')); +#else + return + (w&~0xff)?(true):(((unsigned long)isspace((char)w))||((w == '\r')||(w == '\n'))); +#endif +} + +bool +GStringRep::giswupper(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?(true):((unsigned long)iswupper((wchar_t)w)?true:false); +#else + return (w&~0xff)?(true):((unsigned long)isupper((char)w)?true:false); +#endif +} + +bool +GStringRep::giswlower(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?(true):((unsigned long)iswlower((wchar_t)w)?true:false); +#else + return (w&~0xff)?(true):((unsigned long)islower((char)w)?true:false); +#endif +} + +unsigned long +GStringRep::gtowupper(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?w:((unsigned long)towupper((wchar_t)w)); +#else + return (w&~0xff)?w:((unsigned long)toupper((char)w)); +#endif +} + +unsigned long +GStringRep::gtowlower(const unsigned long w) +{ +#if HAS_WCTYPE + return ((sizeof(wchar_t) == 2)&&(w&~0xffff)) + ?w:((unsigned long)towlower((wchar_t)w)); +#else + return (w&~0xff)?w:((unsigned long)tolower((char)w)); +#endif +} + +GP<GStringRep> +GStringRep::tocase( + bool (*xiswcase)(const unsigned long wc), + unsigned long (*xtowcase)(const unsigned long wc)) const +{ + GP<GStringRep> retval; + char const * const eptr=data+size; + char const *ptr=data; + while(ptr<eptr) + { + char const * const xptr=isCharType(xiswcase,ptr,false); + if(ptr == xptr) + break; + ptr=xptr; + } + if(ptr<eptr) + { + const int n=(int)((size_t)ptr-(size_t)data); + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,n+(1+size-n)*6); + if(n>0) + { + strncpy((char *)buf,data,n); + } + unsigned char *buf_ptr=buf+n; + for(char const *ptr=data+n;ptr<eptr;) + { + char const * const xptr=ptr; + const unsigned long w=getValidUCS4(ptr); + if(ptr == xptr) + break; + if(xiswcase(w)) + { + const int len=(int)((size_t)ptr-(size_t)xptr); + strncpy((char *)buf_ptr,xptr,len); + buf_ptr+=len; + }else + { + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + buf_ptr=UCS4toString(xtowcase(w),buf_ptr,&ps); + } + } + buf_ptr[0]=0; + retval=substr((const char *)buf,0,(int)((size_t)buf_ptr-(size_t)buf)); + }else + { + retval=const_cast<GStringRep *>(this); + } + return retval; +} + +// Returns a copy of this string with characters used in XML escaped as follows: +// '<' --> "<" +// '>' --> ">" +// '&' --> "&" +// '\'' --> "'" +// '\"' --> """ +// Also escapes characters 0x00 through 0x1f and 0x7e through 0x7f. +GP<GStringRep> +GStringRep::toEscaped( const bool tosevenbit ) const +{ + bool modified=false; + char *ret; + GPBuffer<char> gret(ret,size*7); + ret[0]=0; + char *retptr=ret; + char const *start=data; + char const *s=start; + char const *last=s; + GP<GStringRep> special; + for(unsigned long w;(w=getValidUCS4(s));last=s) + { + char const *ss=0; + switch(w) + { + case '<': + ss="<"; + break; + case '>': + ss=">"; + break; + case '&': + ss="&"; + break; + case '\47': + ss="'"; + break; + case '\42': + ss="""; + break; + default: + if((w<' ')||(w>=0x7e && (tosevenbit || (w < 0x80)))) + { + special=toThis(UTF8::create_format("&#%lu;",w)); + ss=special->data; + } + break; + } + if(ss) + { + modified=true; + if(s!=start) + { + size_t len=(size_t)last-(size_t)start; + strncpy(retptr,start,len); + retptr+=len; + start=s; + } + if(ss[0]) + { + size_t len=strlen(ss); + strcpy(retptr,ss); + retptr+=len; + } + } + } + GP<GStringRep> retval; + if(modified) + { + strcpy(retptr,start); + retval=strdup( ret ); + }else + { + retval=const_cast<GStringRep *>(this); + } +// DEBUG_MSG( "Escaped string is '" << ret << "'\n" ); + return retval; +} + + +static const GMap<GUTF8String,GUTF8String> & +BasicMap( void ) +{ + static GMap<GUTF8String,GUTF8String> Basic; + if (! Basic.size()) + { + Basic["lt"] = GUTF8String('<'); + Basic["gt"] = GUTF8String('>'); + Basic["amp"] = GUTF8String('&'); + Basic["apos"] = GUTF8String('\47'); + Basic["quot"] = GUTF8String('\42'); + } + return Basic; +} + +GUTF8String +GUTF8String::fromEscaped( const GMap<GUTF8String,GUTF8String> ConvMap ) const +{ + GUTF8String ret; // Build output string here + int start_locn = 0; // Beginning of substring to skip + int amp_locn; // Location of a found ampersand + + while( (amp_locn = search( '&', start_locn )) > -1 ) + { + // Found the next apostrophe + // Locate the closing semicolon + const int semi_locn = search( ';', amp_locn ); + // No closing semicolon, exit and copy + // the rest of the string. + if( semi_locn < 0 ) + break; + ret += substr( start_locn, amp_locn - start_locn ); + int const len = semi_locn - amp_locn - 1; + if(len) + { + GUTF8String key = substr( amp_locn+1, len); + //DEBUG_MSG( "key = '" << key << "'\n" ); + char const * s=key; + if( s[0] == '#') + { + unsigned long value; + char *ptr=0; + if(s[1] == 'x' || s[1] == 'X') + { + value=strtoul((char const *)(s+2),&ptr,16); + }else + { + value=strtoul((char const *)(s+1),&ptr,10); + } + if(ptr) + { + unsigned char utf8char[7]; + unsigned char const * const end=GStringRep::UCS4toUTF8(value,utf8char); + ret+=GUTF8String((char const *)utf8char,(size_t)end-(size_t)utf8char); + }else + { + ret += substr( amp_locn, semi_locn - amp_locn + 1 ); + } + }else + { + GPosition map_entry = ConvMap.contains( key ); + if( map_entry ) + { // Found in the conversion map, substitute + ret += ConvMap[map_entry]; + } else + { + static const GMap<GUTF8String,GUTF8String> &Basic = BasicMap(); + GPosition map_entry = Basic.contains( key ); + if ( map_entry ) + { + ret += Basic[map_entry]; + }else + { + ret += substr( amp_locn, len+2 ); + } + } + } + }else + { + ret += substr( amp_locn, len+2 ); + } + start_locn = semi_locn + 1; +// DEBUG_MSG( "ret = '" << ret << "'\n" ); + } + + // Copy the end of the string to the output + ret += substr( start_locn, length()-start_locn ); + +// DEBUG_MSG( "Unescaped string is '" << ret << "'\n" ); + return (ret == *this)?(*this):ret; +} + +GUTF8String +GUTF8String::fromEscaped(void) const +{ + const GMap<GUTF8String,GUTF8String> nill; + return fromEscaped(nill); +} + +GP<GStringRep> +GStringRep::setat(int n, char ch) const +{ + GP<GStringRep> retval; + if(n<0) + n+=size; + if (n < 0 || n>size) + GBaseString::throw_illegal_subscript(); + if(ch == data[n]) + { + retval=const_cast<GStringRep *>(this); + }else if(!ch) + { + retval=getbuf(n); + }else + { + retval=getbuf((n<size)?size:n); + retval->data[n]=ch; + if(n == size) + retval->data[n+1]=0; + } + return retval; +} + +#ifdef WIN32 +#define USE_VSNPRINTF _vsnprintf +#endif + +#ifdef AUTOCONF +# ifdef HAVE_VSNPRINTF +# define USE_VSNPRINTF vsnprintf +# endif +#else +# ifdef linux +# define USE_VSNPRINTF vsnprintf +# endif +#endif + +GUTF8String & +GUTF8String::format(const char fmt[], ... ) +{ + va_list args; + va_start(args, fmt); + return init(GStringRep::UTF8::create(fmt,args)); +} + +GP<GStringRep> +GStringRep::UTF8::create_format(const char fmt[],...) +{ + va_list args; + va_start(args, fmt); + return create(fmt,args); +} + +GP<GStringRep> +GStringRep::vformat(va_list args) const +{ + GP<GStringRep> retval; + if(size) + { +#ifndef WIN32 + char *nfmt; + GPBuffer<char> gnfmt(nfmt,size+1); + nfmt[0]=0; + int start=0; +#endif + int from=0; + while((from=search('%',from)) >= 0) + { + if(data[++from] != '%') + { + int m,n=0; + sscanf(data+from,"%d!%n",&m,&n); + if(n) + { +#ifdef WIN32 + char *lpszFormat=data; + LPTSTR lpszTemp; + if((!::FormatMessage( + FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, + lpszFormat, 0, 0, (LPTSTR)&lpszTemp,0,&args)) + || !lpszTemp) + { + G_THROW(GException::outofmemory); + } + va_end(args); + retval=strdup((const char *)lpszTemp); + LocalFree(lpszTemp); + break; +#else + from+=n; + const int end=search('!',from); + if(end>=0) + { + strncat(nfmt,data+start,(int)(end-start)); + strncat(nfmt,"$",1); + start=from=end+1; + }else + { + gnfmt.resize(0); + from=(-1); + break; + } +#endif + }else + { +#ifndef WIN32 + gnfmt.resize(0); +#endif + from=(-1); + break; + } + } + } + if(from < 0) + { +#ifndef WIN32 + char const * const fmt=(nfmt&&nfmt[0])?nfmt:data; +#else + char const * const fmt=data; +#endif + int buflen=32768; + char *buffer; + GPBuffer<char> gbuffer(buffer,buflen); + + ChangeLocale locale(LC_NUMERIC,(isNative()?0:"C")); + + // Format string +#ifdef USE_VSNPRINTF + while(USE_VSNPRINTF(buffer, buflen, fmt, args)<0) + { + gbuffer.resize(0); + gbuffer.resize(buflen+32768); + } + va_end(args); +#else + buffer[buflen-1] = 0; + vsprintf(buffer, fmt, args); + va_end(args); + if (buffer[buflen-1]) + { + // This isn't as fatal since it is on the stack, but we + // definitely should stop the current operation. + G_THROW( ERR_MSG("GString.overwrite") ); + } +#endif + retval=strdup((const char *)buffer); + } + } + // Go altering the string + return retval; +} + +int +GStringRep::search(char c, int from) const +{ + if (from<0) + from += size; + int retval=(-1); + if (from>=0 && from<size) + { + char const *const s = strchr(data+from,c); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::search(char const *ptr, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if (from>=0 && from<size) + { + char const *const s = strstr(data+from,ptr); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::rsearch(char c, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if ((from>=0) && (from<size)) + { + char const *const s = strrchr(data+from,c); + if(s) + retval=(int)((size_t)s-(size_t)data); + } + return retval; +} + +int +GStringRep::rsearch(char const *ptr, int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + for(int loc=from;(loc=search(ptr,loc)) >= 0;++loc) + retval=loc; + return retval; +} + +int +GStringRep::contains(const char accept[],int from) const +{ + if(from<0) + { + from+=size; + if(from<0) + G_THROW( ERR_MSG("GString.bad_subscript") ); + } + int retval=(-1); + if (accept && accept[0] && from>=0 && from<size) + { + char const * const src = data+from; + char const *ptr=strpbrk(src,accept); + if(ptr) + { + retval=(int)(ptr-src)+from; + } + } + return retval; +} + +int +GStringRep::rcontains(const char accept[],int from) const +{ + int retval=(-1); + while((from=contains(accept,from)) >= 0) + { + retval=from++; + } + return retval; +} + +bool +GBaseString::is_int(void) const +{ + bool isLong=!!ptr; + if(isLong) + { + int endpos; + (*this)->toLong(0,endpos); + if(endpos>=0) + { + isLong=((*this)->nextNonSpace(endpos) == (int)length()); + } + } + return isLong; +} + +bool +GBaseString::is_float(void) const +{ + bool isDouble=!!ptr; + if(isDouble) + { + int endpos; + (*this)->toDouble(0,endpos); + if(endpos>=0) + { + isDouble=((*this)->nextNonSpace(endpos) == (int)length()); + } + } + return isDouble; +} + +unsigned int +hash(const GBaseString &str) +{ + unsigned int x = 0; + const char *s = (const char*)str; + while (*s) + x = x ^ (x<<6) ^ (unsigned char)(*s++); + return x; +} + +void +GBaseString::throw_illegal_subscript() +{ + G_THROW( ERR_MSG("GString.bad_subscript") ); +} + +unsigned char * +GStringRep::UTF8::UCS4toString( + const unsigned long w0,unsigned char *ptr, mbstate_t *) const +{ + return UCS4toUTF8(w0,ptr); +} + +int +GStringRep::UTF8::ncopy( + wchar_t * const buf, const int buflen ) const +{ + int retval=(-1); + if(buf && buflen) + { + buf[0]=0; + if(data[0]) + { + const size_t length=strlen(data); + const unsigned char * const eptr=(const unsigned char *)(data+length); + wchar_t *r=buf; + wchar_t const * const rend=buf+buflen; + for(const unsigned char *s=(const unsigned char *)data;(r<rend)&&(s<eptr)&&*s;) + { + const unsigned long w0=UTF8toUCS4(s,eptr); + unsigned short w1; + unsigned short w2=1; + for(int count=(sizeof(wchar_t) == sizeof(w1))?UCS4toUTF16(w0,w1,w2):1; + count&&(r<rend); + --count,w1=w2,++r) + { + r[0]=(sizeof(wchar_t) == sizeof(w1))?(wchar_t)w1:(wchar_t)w0; + } + } + if(r<rend) + { + r[0]=0; + retval=((size_t)r-(size_t)buf)/sizeof(wchar_t); + } + }else + { + retval=0; + } + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::toNative(const EscapeMode escape) const +{ + GP<GStringRep> retval; + if(data[0]) + { + const size_t length=strlen(data); + const unsigned char * const eptr=(const unsigned char *)(data+length); + unsigned char *buf; + GPBuffer<unsigned char> gbuf(buf,12*length+12); + unsigned char *r=buf; + mbstate_t ps; + memset(&ps,0,sizeof(mbstate_t)); + for(const unsigned char *s=(const unsigned char *)data;(s<eptr)&& *s;) + { + const unsigned long w0=UTF8toUCS4(s,eptr); + const unsigned char * const r0=r; + r=UCS4toNative(w0,r,&ps); + if(r == r0) + { + if(escape == IS_ESCAPED) + { + sprintf((char *)r,"&#%lu;",w0); + r+=strlen((char *)r); + }else + { + r=buf; + break; + } + } + } + r[0]=0; + retval = NATIVE_CREATE( (const char *)buf ); + } else + { + retval = NATIVE_CREATE( (unsigned int)0 ); + } + return retval; +} + +GP<GStringRep> +GStringRep::UTF8::toUTF8(const bool nothrow) const +{ + if(!nothrow) + G_THROW( ERR_MSG("GStringRep.UTF8ToUTF8") ); + return const_cast<GStringRep::UTF8 *>(this); +} + +// Tests if a string is legally encoded in the current character set. +bool +GStringRep::UTF8::is_valid(void) const +{ + bool retval=true; + if(data && size) + { + const unsigned char * const eptr=(const unsigned char *)(data+size); + for(const unsigned char *s=(const unsigned char *)data;(s<eptr)&& *s;) + { + const unsigned char * const r=s; + (void)UTF8toUCS4(s,eptr); + if(r == s) + { + retval=false; + break; + } + } + } + return retval; +} + +static inline unsigned long +add_char(unsigned long const U, unsigned char const * const r) +{ + unsigned long const C=r[0]; + return ((C|0x3f) == 0xbf)?((U<<6)|(C&0x3f)):0; +} + +unsigned long +GStringRep::UTF8toUCS4( + unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const *r=s; + if(r < eptr) + { + unsigned long const C1=r++[0]; + if(C1&0x80) + { + if(r < eptr) + { + U=C1; + if((U=((C1&0x40)?add_char(U,r++):0))) + { + if(C1&0x20) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x10) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x8) + { + if(r < eptr) + { + if((U=add_char(U,r++))) + { + if(C1&0x4) + { + if(r < eptr) + { + if((U=((!(C1&0x2))?(add_char(U,r++)&0x7fffffff):0))) + { + s=r; + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x4000000)?0:(U&0x3ffffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x200000)?0:(U&0x1fffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x10000)?0:(U&0xffff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=((U&0x800)?0:(U&0x7ff)))) + { + s=r; + } + }else + { + U=(unsigned int)(-1)-s++[0]; + } + }else + { + U=0; + } + }else if((U=C1)) + { + s=r; + } + } + return U; +} + +unsigned char * +GStringRep::UCS4toUTF8(const unsigned long w,unsigned char *ptr) +{ + if(w <= 0x7f) + { + *ptr++ = (unsigned char)w; + } + else if(w <= 0x7ff) + { + *ptr++ = (unsigned char)((w>>6)|0xC0); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0xFFFF) + { + *ptr++ = (unsigned char)((w>>12)|0xE0); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x1FFFFF) + { + *ptr++ = (unsigned char)((w>>18)|0xF0); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x3FFFFFF) + { + *ptr++ = (unsigned char)((w>>24)|0xF8); + *ptr++ = (unsigned char)(((w>>18)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else if(w <= 0x7FFFFFFF) + { + *ptr++ = (unsigned char)((w>>30)|0xFC); + *ptr++ = (unsigned char)(((w>>24)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>18)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>12)|0x80)&0xBF); + *ptr++ = (unsigned char)(((w>>6)|0x80)&0xBF); + *ptr++ = (unsigned char)((w|0x80)&0xBF); + } + else + { + *ptr++ = '?'; + } + return ptr; +} + + // Creates with a concat operation. +GP<GStringRep> +GStringRep::concat( const char *s1, const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s2) + { + retval=toThis(s2); + if(s1 && s1[0]) + { + if(retval) + { + retval=concat(s1,retval->data); + }else + { + retval=strdup(s1); + } + } + }else if(s1 && s1[0]) + { + retval=strdup(s1); + } + return retval; +} + + // Creates with a concat operation. + +GP<GStringRep> +GStringRep::concat( const GP<GStringRep> &s1,const char *s2) const +{ + GP<GStringRep> retval; + if(s1) + { + retval=toThis(s1); + if(s2 && s2[0]) + { + if(retval) + { + retval=retval->append(s2); + }else + { + retval=strdup(s2); + } + } + }else if(s2 && s2[0]) + { + retval=strdup(s2); + } + return retval; +} + +GP<GStringRep> +GStringRep::concat(const GP<GStringRep> &s1,const GP<GStringRep> &s2) const +{ + GP<GStringRep> retval; + if(s1) + { + retval=toThis(s1,s2); + if(retval && s2) + { + retval=retval->append(toThis(s2)); + } + }else if(s2) + { + retval=toThis(s2); + } + return retval; +} + +#ifdef WIN32 +static const char *setlocale_win32(void) +{ + static const char *locale=setlocale(LC_ALL,0); + if(! locale || (locale[0] == 'C' && !locale[1])) + { + locale=setlocale(LC_ALL,""); + } + return locale; +} +#endif + +GStringRep::GStringRep(void) +{ +#ifdef WIN32 + static const char *locale=setlocale_win32(); +#endif + size=0; + data=0; +} + +GStringRep::~GStringRep() +{ + if(data) + { + data[0]=0; + ::operator delete(data); + } + data=0; +} + +GStringRep::UTF8::UTF8(void) {} + +GStringRep::UTF8::~UTF8() {} + +int +GStringRep::cmp(const char *s1,const int len) const +{ + return cmp(data,s1,len); +} + +int +GStringRep::cmp(const char *s1, const char *s2,const int len) +{ + return (len + ?((s1&&s1[0]) + ?((s2&&s2[0]) + ?((len>0) + ?strncmp(s1,s2,len) + :strcmp(s1,s2)) + :1) + :((s2&&s2[0])?(-1):0)) + :0); +} + +int +GStringRep::cmp(const GP<GStringRep> &s1, const GP<GStringRep> &s2, + const int len ) +{ + return (s1?(s1->cmp(s2,len)):cmp(0,(s2?(s2->data):0),len)); +} + +int +GStringRep::cmp(const GP<GStringRep> &s1, const char *s2, + const int len ) +{ + return cmp((s1?s1->data:0),s2,len); +} + +int +GStringRep::cmp(const char *s1, const GP<GStringRep> &s2, + const int len ) +{ + return cmp(s1,(s2?(s2->data):0),len); +} + +int +GStringRep::UTF8::cmp(const GP<GStringRep> &s2,const int len) const +{ + int retval; + if(s2) + { + if(s2->isNative()) + { + GP<GStringRep> r(s2->toUTF8(true)); + if(r) + { + retval=GStringRep::cmp(data,r->data,len); + }else + { + retval=-(s2->cmp(toNative(NOT_ESCAPED),len)); + } + }else + { + retval=GStringRep::cmp(data,s2->data,len); + } + }else + { + retval=GStringRep::cmp(data,0,len); + } + return retval; +} + +int +GStringRep::UTF8::toInt() const +{ + int endpos; + return (int)toLong(0,endpos); +} + +static inline long +Cstrtol(char *data,char **edata, const int base) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtol(data,edata,base); +} + +long +GStringRep::UTF8::toLong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + long retval=Cstrtol(data+pos,&edata, base); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toLong(0,xendpos,base); + if(xendpos> 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +static inline unsigned long +Cstrtoul(char *data,char **edata, const int base) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtoul(data,edata,base); +} + +unsigned long +GStringRep::UTF8::toULong( + const int pos, int &endpos, const int base) const +{ + char *edata=0; + unsigned long retval=Cstrtoul(data+pos,&edata, base); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toULong(0,xendpos,base); + if(xendpos> 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +static inline double +Cstrtod(char *data,char **edata) +{ + GStringRep::ChangeLocale locale(LC_NUMERIC,"C"); + while (data && *data==' ') data++; + return strtod(data,edata); +} + +double +GStringRep::UTF8::toDouble(const int pos, int &endpos) const +{ + char *edata=0; + double retval=Cstrtod(data+pos,&edata); + if(edata) + { + endpos=edata-data; + }else + { + endpos=(-1); + GP<GStringRep> ptr=ptr->strdup(data+pos); + if(ptr) + ptr=ptr->toNative(NOT_ESCAPED); + if(ptr) + { + int xendpos; + retval=ptr->toDouble(0,xendpos); + if(xendpos >= 0) + { + endpos=(int)size; + ptr=ptr->strdup(data+xendpos); + if(ptr) + { + ptr=ptr->toUTF8(true); + if(ptr) + { + endpos-=(int)(ptr->size); + } + } + } + } + } + return retval; +} + +int +GStringRep::getUCS4(unsigned long &w, const int from) const +{ + int retval; + if(from>=size) + { + w=0; + retval=size; + }else if(from<0) + { + w=(unsigned int)(-1); + retval=(-1); + }else + { + const char *source=data+from; + w=getValidUCS4(source); + retval=(int)((size_t)source-(size_t)data); + } + return retval; +} + + +unsigned long +GStringRep::UTF8::getValidUCS4(const char *&source) const +{ + return GStringRep::UTF8toUCS4((const unsigned char *&)source,data+size); +} + +int +GStringRep::nextNonSpace(const int from,const int len) const +{ + return nextCharType(giswspace,from,len,true); +} + +int +GStringRep::nextSpace(const int from,const int len) const +{ + return nextCharType(giswspace,from,len,false); +} + +int +GStringRep::nextChar(const int from) const +{ + char const * xptr=data+from; + (void)getValidUCS4(xptr); + return (int)((size_t)xptr-(size_t)data); +} + +int +GStringRep::firstEndSpace(int from,const int len) const +{ + const int xsize=(len<0)?size:(from+len); + const int ysize=(size<xsize)?size:xsize; + int retval=ysize; + while(from<ysize) + { + from=nextNonSpace(from,ysize-from); + if(from < size) + { + const int r=nextSpace(from,ysize-from); + // If a character isn't legal, then it will return + // tru for both nextSpace and nextNonSpace. + if(r == from) + { + from++; + }else + { + from=retval=r; + } + } + } + return retval; +} + +int +GStringRep::UCS4toUTF16( + const unsigned long w,unsigned short &w1, unsigned short &w2) +{ + int retval; + if(w<0x10000) + { + w1=(unsigned short)w; + w2=0; + retval=1; + }else + { + w1=(unsigned short)((((w-0x10000)>>10)&0x3ff)+0xD800); + w2=(unsigned short)((w&0x3ff)+0xDC00); + retval=2; + } + return retval; +} + +int +GStringRep::UTF16toUCS4( + unsigned long &U,unsigned short const * const s,void const * const eptr) +{ + int retval=0; + U=0; + unsigned short const * const r=s+1; + if(r <= eptr) + { + unsigned long const W1=s[0]; + if((W1<0xD800)||(W1>0xDFFF)) + { + if((U=W1)) + { + retval=1; + } + }else if(W1<=0xDBFF) + { + unsigned short const * const rr=r+1; + if(rr <= eptr) + { + unsigned long const W2=s[1]; + if(((W2>=0xDC00)||(W2<=0xDFFF))&&((U=(0x10000+((W1&0x3ff)<<10))|(W2&0x3ff)))) + { + retval=2; + }else + { + retval=(-1); + } + } + } + } + return retval; +} + + +//bcr + +GUTF8String& +GUTF8String::operator+= (char ch) +{ + return init( + GStringRep::UTF8::create((const char*)*this, + GStringRep::UTF8::create(&ch,0,1))); +} + +GUTF8String& +GUTF8String::operator+= (const char *str) +{ + return init(GStringRep::UTF8::create(*this,str)); +} + +GUTF8String& +GUTF8String::operator+= (const GBaseString &str) +{ + return init(GStringRep::UTF8::create(*this,str)); +} + +GUTF8String +GUTF8String::substr(int from, int len) const +{ return GUTF8String(*this, from, len); } + +GUTF8String +GUTF8String::operator+(const GBaseString &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +GUTF8String +GUTF8String::operator+(const GUTF8String &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +GUTF8String +GUTF8String::operator+(const char *s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +char * +GUTF8String::getbuf(int n) +{ + if(ptr) + init((*this)->getbuf(n)); + else if(n>0) + init(GStringRep::UTF8::create(n)); + else + init(0); + return ptr?((*this)->data):0; +} + +void +GUTF8String::setat(const int n, const char ch) +{ + if((!n)&&(!ptr)) + { + init(GStringRep::UTF8::create(&ch,0,1)); + }else + { + init((*this)->setat(CheckSubscript(n),ch)); + } +} + +GP<GStringRep> +GStringRep::UTF8ToNative( const char *s, const EscapeMode escape ) +{ + return GStringRep::UTF8::create(s)->toNative(escape); +} + +GUTF8String::GUTF8String(const char dat) +{ init(GStringRep::UTF8::create(&dat,0,1)); } + +GUTF8String::GUTF8String(const GUTF8String &fmt, va_list &args) +{ + if (fmt.ptr) + init(fmt->vformat(args)); + else + init(fmt); +} + +GUTF8String::GUTF8String(const char *str) +{ init(GStringRep::UTF8::create(str)); } + +GUTF8String::GUTF8String(const unsigned char *str) +{ init(GStringRep::UTF8::create((const char *)str)); } + +GUTF8String::GUTF8String(const unsigned short *str) +{ init(GStringRep::UTF8::create(str,0,-1)); } + +GUTF8String::GUTF8String(const unsigned long *str) +{ init(GStringRep::UTF8::create(str,0,-1)); } + +GUTF8String::GUTF8String(const char *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const unsigned short *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const unsigned long *dat, unsigned int len) +{ init(GStringRep::UTF8::create(dat,0,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const GBaseString &gs, int from, int len) +{ init(GStringRep::UTF8::create(gs,from,((int)len<0)?(-1):(int)len)); } + +GUTF8String::GUTF8String(const int number) +{ init(GStringRep::UTF8::create_format("%d",number)); } + +GUTF8String::GUTF8String(const double number) +{ init(GStringRep::UTF8::create_format("%f",number)); } + +GUTF8String& GUTF8String::operator= (const char str) +{ return init(GStringRep::UTF8::create(&str,0,1)); } + +GUTF8String& GUTF8String::operator= (const char *str) +{ return init(GStringRep::UTF8::create(str)); } + +GUTF8String GBaseString::operator+(const GUTF8String &s2) const +{ return GStringRep::UTF8::create(*this,s2); } + +#if HAS_WCHAR +GUTF8String +GNativeString::operator+(const GUTF8String &s2) const +{ + if (ptr) + return GStringRep::UTF8::create((*this)->toUTF8(true),s2); + else + return GStringRep::UTF8::create((*this),s2); +} +#endif + +GUTF8String +GUTF8String::operator+(const GNativeString &s2) const +{ + GP<GStringRep> g = s2; + if (s2.ptr) + g = s2->toUTF8(true); + return GStringRep::UTF8::create(*this,g); +} + +GUTF8String +operator+(const char *s1, const GUTF8String &s2) +{ return GStringRep::UTF8::create(s1,s2); } + +#if HAS_WCHAR +GNativeString +operator+(const char *s1, const GNativeString &s2) +{ return GStringRep::Native::create(s1,s2); } + +GNativeString& +GNativeString::operator+= (char ch) +{ + char s[2]; s[0]=ch; s[1]=0; + return init(GStringRep::Native::create((const char*)*this, s)); +} + +GNativeString& +GNativeString::operator+= (const char *str) +{ + return init(GStringRep::Native::create(*this,str)); +} + +GNativeString& +GNativeString::operator+= (const GBaseString &str) +{ + return init(GStringRep::Native::create(*this,str)); +} + +GNativeString +GNativeString::operator+(const GBaseString &s2) const +{ return GStringRep::Native::create(*this,s2); } + +GNativeString +GNativeString::operator+(const GNativeString &s2) const +{ return GStringRep::Native::create(*this,s2); } + +GNativeString +GNativeString::operator+(const char *s2) const +{ return GStringRep::Native::create(*this,s2); } + +char * +GNativeString::getbuf(int n) +{ + if(ptr) + init((*this)->getbuf(n)); + else if(n>0) + init(GStringRep::Native::create(n)); + else + init(0); + return ptr?((*this)->data):0; +} + +void +GNativeString::setat(const int n, const char ch) +{ + if((!n)&&(!ptr)) + { + init(GStringRep::Native::create(&ch,0,1)); + }else + { + init((*this)->setat(CheckSubscript(n),ch)); + } +} + +#endif + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GString.h b/kviewshell/plugins/djvu/libdjvu/GString.h new file mode 100644 index 00000000..601db983 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GString.h @@ -0,0 +1,1676 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GString.h,v 1.19 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GSTRING_H_ +#define _GSTRING_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name GString.h + + Files #"GString.h"# and #"GString.cpp"# implement a general + purpose string class \Ref{GBaseString}, with dirived types + \Ref{GUTF8String} and \Ref{GNativeString} for UTF8 MBS encoding + and the current Native MBS encoding respectively. This + implementation relies on smart pointers (see + \Ref{GSmartPointer.h}). + + {\bf Historical Comments} --- At some point during the DjVu + research era, it became clear that C++ compilers rarely provided + portable libraries. We then decided to avoid fancy classes (like + #iostream# or #string#) and to rely only on the good old C + library. A good string class however is very useful. We had + already randomly picked letter 'G' to prefix class names and we + logically derived the new class name. Native English speakers + kept laughing in hiding. This is ironic because we completely + forgot this letter 'G' when creating more challenging things + like the ZP Coder or the IW44 wavelets. + + {\bf Later Changes} + When converting to I18N, we (Lizardtech) decided that two string classes + where needing, replacing the original GString with \Ref{GUTF8String} and + \Ref{GNativeString}. + + @memo + General purpose string class. + @author + L\'eon Bottou <[email protected]> -- initial implementation.\\ + +// From: Leon Bottou, 1/31/2002 +// This file has very little to do with my initial implementation. +// It has been practically rewritten by Lizardtech for i18n changes. +// My original implementation was very small in comparison +// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>. +// In my opinion, the duplication of the string classes is a failed +// attempt to use the type system to enforce coding policies. +// This could be fixed. But there are better things to do in djvulibre. + + @version + #$Id: GString.h,v 1.19 2004/08/06 15:11:29 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include "GContainer.h" + +#include <stdlib.h> +#include <stdarg.h> +#ifdef WIN32 +# include <windows.h> +# define HAS_WCHAR 1 +# define HAS_MBSTATE 1 +#endif + +#if HAS_WCHAR +# if !defined(AUTOCONF) || HAVE_WCHAR_H +# include <wchar.h> +# endif +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +#if !HAS_MBSTATE +# ifndef HAVE_MBSTATE_T +typedef int mbstate_t; +# endif +#endif + +class GBaseString; + +// Internal string representation. +class GStringRep : public GPEnabled +{ +public: + enum EncodeType { XUCS4, XUCS4BE, XUCS4LE, XUCS4_2143, XUCS4_3412, + XUTF16, XUTF16BE, XUTF16LE, XUTF8, XEBCDIC, XOTHER } ; + + enum EscapeMode { UNKNOWN_ESCAPED=0, IS_ESCAPED=1, NOT_ESCAPED=2 }; + + class UTF8; + friend class UTF8; + class Unicode; + friend class Unicode; + + class ChangeLocale; +#if HAS_WCHAR + class Native; + friend class Native; +#endif // HAS_WCHAR + friend class GBaseString; + friend class GUTF8String; + friend class GNativeString; + friend unsigned int hash(const GBaseString &ref); + +public: + // default constructor + GStringRep(void); + // virtual destructor + virtual ~GStringRep(); + + // Other virtual methods. + // Create an empty string. + virtual GP<GStringRep> blank(const unsigned int sz) const = 0; + // Create a duplicate at the given size. + GP<GStringRep> getbuf(int n) const; + // Change the value of one of the bytes. + GP<GStringRep> setat(int n, char ch) const; + // Append a string. + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const = 0; + // Test if isUTF8. + virtual bool isUTF8(void) const { return false; } + // Test if Native. + virtual bool isNative(void) const { return false; } + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED ) const = 0; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const = 0; + // Convert to same as current class. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &locale=0) const = 0; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2,const int len=(-1)) const = 0; + + // Convert strings to numbers. + virtual int toInt(void) const = 0; + virtual long int toLong( + const int pos, int &endpos, const int base=10) const = 0; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const = 0; + virtual double toDouble(const int pos, int &endpos) const = 0; + + // return the position of the next character + int nextChar( const int from=0 ) const; + + // return next non space position + int nextNonSpace( const int from=0, const int len=(-1) ) const; + + // return next white space position + int nextSpace( const int from=0, const int len=(-1) ) const; + + // return the position after the last non-whitespace character. + int firstEndSpace( int from=0, const int len=(-1) ) const; + + // Create an empty string. + template <class TYPE> static GP<GStringRep> create( + const unsigned int sz,TYPE *); + // Creates with a strdup string. + GP<GStringRep> strdup(const char *s) const; + + // Creates by appending to the current string + GP<GStringRep> append(const char *s2) const; + + // Creates with a concat operation. + GP<GStringRep> concat(const GP<GStringRep> &s1,const GP<GStringRep> &s2) const; + GP<GStringRep> concat(const char *s1,const GP<GStringRep> &s2) const; + GP<GStringRep> concat(const GP<GStringRep> &s1,const char *s2) const; + GP<GStringRep> concat(const char *s1,const char *s2) const; + + /* Creates with a strdup and substr. Negative values have strlen(s)+1 + added to them. + */ + GP<GStringRep> substr( + const char *s,const int start,const int length=(-1)) const; + + GP<GStringRep> substr( + const unsigned short *s,const int start,const int length=(-1)) const; + + GP<GStringRep> substr( + const unsigned long *s,const int start,const int length=(-1)) const; + + /** Initializes a string with a formatted string (as in #vprintf#). The + string is re-initialized with the characters generated according to the + specified format #fmt# and using the optional arguments. See the ANSI-C + function #vprintf()# for more information. The current implementation + will cause a segmentation violation if the resulting string is longer + than 32768 characters. */ + GP<GStringRep> vformat(va_list args) const; + // -- SEARCHING + + static GP<GStringRep> UTF8ToNative( const char *s, + const EscapeMode escape=UNKNOWN_ESCAPED ); + static GP<GStringRep> NativeToUTF8( const char *s ); + + // Creates an uppercase version of the current string. + GP<GStringRep> upcase(void) const; + // Creates a lowercase version of the current string. + GP<GStringRep> downcase(void) const; + + /** Returns the next UCS4 character, and updates the pointer s. */ + static unsigned long UTF8toUCS4( + unsigned char const *&s, void const * const endptr ); + + /** Returns the number of bytes in next UCS4 character, + and sets #w# to the next UCS4 chacter. */ + static int UTF8toUCS4( + unsigned long &w, unsigned char const s[], void const * const endptr ) + { unsigned char const *r=s;w=UTF8toUCS4(r,endptr);return (int)((size_t)r-(size_t)s); } + + /** Returns the next UCS4 word from the UTF16 string. */ + static int UTF16toUCS4( + unsigned long &w, unsigned short const * const s,void const * const eptr); + + static int UCS4toUTF16( + unsigned long w, unsigned short &w1, unsigned short &w2); + + int cmp(const char *s2, const int len=(-1)) const; + static int cmp( + const GP<GStringRep> &s1, const GP<GStringRep> &s2, const int len=(-1)) ; + static int cmp( + const GP<GStringRep> &s1, const char *s2, const int len=(-1)); + static int cmp( + const char *s1, const GP<GStringRep> &s2, const int len=(-1)); + static int cmp( + const char *s1, const char *s2, const int len=(-1)); + + // Lookup the next character, and return the position of the next character. + int getUCS4(unsigned long &w, const int from) const; + + virtual unsigned char *UCS4toString( + const unsigned long w, unsigned char *ptr, mbstate_t *ps=0) const = 0; + + static unsigned char *UCS4toUTF8( + const unsigned long w,unsigned char *ptr); + + static unsigned char *UCS4toNative( + const unsigned long w,unsigned char *ptr, mbstate_t *ps); + + int search(char c, int from=0) const; + + int search(char const *str, int from=0) const; + + int rsearch(char c, int from=0) const; + + int rsearch(char const *str, int from=0) const; + + int contains(char const accept[], int from=0) const; + + int rcontains(char const accept[], int from=0) const; + +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const = 0; + + GP<GStringRep> tocase( + bool (*xiswcase)(const unsigned long wc), + unsigned long (*xtowcase)(const unsigned long wc)) const; + + // Tests if the specified character passes the xiswtest. If so, the + // return pointer is incremented to the next character, otherwise the + // specified #ptr# is returned. + const char * isCharType( bool (*xiswtest)(const unsigned long wc), const char *ptr, + const bool reverse=false) const; + + // Find the next character position that passes the isCharType test. + int nextCharType( + bool (*xiswtest)(const unsigned long wc),const int from,const int len, + const bool reverse=false) const; + + static bool giswspace(const unsigned long w); + static bool giswupper(const unsigned long w); + static bool giswlower(const unsigned long w); + static unsigned long gtowupper(const unsigned long w); + static unsigned long gtowlower(const unsigned long w); + + virtual void set_remainder( void const * const buf, const unsigned int size, + const EncodeType encodetype); + virtual void set_remainder( void const * const buf, const unsigned int size, + const GP<GStringRep> &encoding ); + virtual void set_remainder ( const GP<Unicode> &remainder ); + + virtual GP<Unicode> get_remainder( void ) const; + +public: + /* Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GP<GStringRep> toEscaped( const bool tosevenbit ) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const = 0; + + virtual int ncopy(wchar_t * const buf, const int buflen) const = 0; + +protected: + +// Actual string data. + int size; + char *data; +}; + +class GStringRep::UTF8 : public GStringRep +{ +public: + // default constructor + UTF8(void); + // virtual destructor + virtual ~UTF8(); + + // Other virtual methods. + virtual GP<GStringRep> blank(const unsigned int sz = 0) const; + virtual GP<GStringRep> append(const GP<GStringRep> &s2) const; + // Test if Native. + virtual bool isUTF8(void) const; + // Convert to Native. + virtual GP<GStringRep> toNative( + const EscapeMode escape=UNKNOWN_ESCAPED) const; + // Convert to UTF8. + virtual GP<GStringRep> toUTF8(const bool nothrow=false) const; + // Convert to same as current class. + virtual GP<GStringRep> toThis( + const GP<GStringRep> &rep,const GP<GStringRep> &) const; + // Compare with #s2#. + virtual int cmp(const GP<GStringRep> &s2,const int len=(-1)) const; + + static GP<GStringRep> create(const unsigned int sz = 0); + + // Convert strings to numbers. + virtual int toInt(void) const; + virtual long int toLong( + const int pos, int &endpos, const int base=10) const; + virtual unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + virtual double toDouble( + const int pos, int &endpos) const; + + // Create a strdup string. + static GP<GStringRep> create(const char *s); + + // Creates with a concat operation. + static GP<GStringRep> create( + const GP<GStringRep> &s1,const GP<GStringRep> &s2); + static GP<GStringRep> create( const GP<GStringRep> &s1,const char *s2); + static GP<GStringRep> create( const char *s1, const GP<GStringRep> &s2); + static GP<GStringRep> create( const char *s1,const char *s2); + + // Create with a strdup and substr operation. + static GP<GStringRep> create( + const char *s,const int start,const int length=(-1)); + + static GP<GStringRep> create( + const unsigned short *s,const int start,const int length=(-1)); + + static GP<GStringRep> create( + const unsigned long *s,const int start,const int length=(-1)); + + static GP<GStringRep> create_format(const char fmt[],...); + static GP<GStringRep> create(const char fmt[],va_list& args); + + virtual unsigned char *UCS4toString( + const unsigned long w,unsigned char *ptr, mbstate_t *ps=0) const; + + // Tests if a string is legally encoded in the current character set. + virtual bool is_valid(void) const; + + virtual int ncopy(wchar_t * const buf, const int buflen) const; + + friend class GBaseString; + +protected: + // Return the next character and increment the source pointer. + virtual unsigned long getValidUCS4(const char *&source) const; +}; + +class GUTF8String; +class GNativeString; + +/** General purpose character string. + Each dirivied instance of class #GBaseString# represents a + character string. Overloaded operators provide a value semantic + to #GBaseString# objects. Conversion operators and constructors + transparently convert between #GBaseString# objects and + #const char*# pointers. The #GBaseString# class has no public + constructors, since a dirived type should always be used + to specify the desired multibyte character encoding. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with dirived #GBaseString# objects since there is a fast + conversion operator from the dirivied #GBaseString# objects + to "#const char*#". Functions returning strings should return + #GUTF8String# or #GNativeString# objects because the class will + automatically manage the necessary memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GBaseString : protected GP<GStringRep> +{ +public: + enum EscapeMode { + UNKNOWN_ESCAPED=GStringRep::UNKNOWN_ESCAPED, + IS_ESCAPED=GStringRep::IS_ESCAPED, + NOT_ESCAPED=GStringRep::NOT_ESCAPED }; + + friend class GUTF8String; + friend class GNativeString; +protected: + // Sets the gstr pointer; + void init(void); + + ~GBaseString(); + GBaseString &init(const GP<GStringRep> &rep); + + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GBaseString( void ); + +public: + // -- ACCESS + /** Converts a string into a constant null terminated character + array. This conversion operator is very efficient because + it simply returns a pointer to the internal string data. The + returned pointer remains valid as long as the string is + unmodified. */ + operator const char* ( void ) const ; + /// Returns the string length. + unsigned int length( void ) const; + /** Returns true if and only if the string contains zero characters. + This operator is useful for conditional expression in control + structures. + \begin{verbatim} + if (! str) { ... } + while (!! str) { ... } -- Note the double operator! + \end{verbatim} + Class #GBaseString# does not to support syntax + "#if# #(str)# #{}#" because the required conversion operator + introduces dangerous ambiguities with certain compilers. */ + bool operator! ( void ) const; + + // -- INDEXING + /** Returns the character at position #n#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len-1#, where #len# is the length of the string. The first + character of a string is numbered zero. Negative positions + represent characters relative to the end of the string. */ + char operator[] (int n) const; + /// Returns #TRUE# if the string contains an integer number. + bool is_int(void) const; + /// Returns #TRUE# if the string contains a float number. + bool is_float(void) const; + + /** Converts strings between native & UTF8 **/ + GNativeString getUTF82Native( EscapeMode escape=UNKNOWN_ESCAPED ) const; + GUTF8String getNative2UTF8( void ) const; + + // -- ALTERING + /// Reinitializes a string with the null string. + void empty( void ); + // -- SEARCHING + /** Searches character #c# in the string, starting at position + #from# and scanning forward until reaching the end of the + string. This function returns the position of the matching + character. It returns #-1# if character #c# cannot be found. */ + int search(char c, int from=0) const; + + /** Searches sub-string #str# in the string, starting at position + #from# and scanning forward until reaching the end of the + string. This function returns the position of the first + matching character of the sub-string. It returns #-1# if + string #str# cannot be found. */ + int search(const char *str, int from=0) const; + + /** Searches character #c# in the string, starting at position + #from# and scanning backwards until reaching the beginning of + the string. This function returns the position of the matching + character. It returns #-1# if character #c# cannot be found. */ + int rsearch(char c, const int from=0) const; + /** Searches sub-string #str# in the string, starting at position + #from# and scanning backwards until reaching the beginning of + the string. This function returns the position of the first + matching character of the sub-string. It returns #-1# if + string #str# cannot be found. */ + int rsearch(const char *str, const int from=0) const; + /** Searches for any of the specified characters in the accept + string. It returns #-1# if the none of the characters and + be found, otherwise the position of the first match. */ + int contains(const char accept[], const int from=0) const; + /** Searches for any of the specified characters in the accept + string. It returns #-1# if the none of the characters and be + found, otherwise the position of the last match. */ + int rcontains(const char accept[], const int from=0) const; + + /** Concatenates strings. Returns a string composed by concatenating + the characters of strings #s1# and #s2#. */ + GUTF8String operator+(const GUTF8String &s2) const; + GNativeString operator+(const GNativeString &s2) const; + + /** Returns an integer. Implements i18n atoi. */ + int toInt(void) const; + + /** Returns a long intenger. Implments i18n strtol. */ + long toLong(const int pos, int &endpos, const int base=10) const; + + /** Returns a unsigned long integer. Implements i18n strtoul. */ + unsigned long toULong( + const int pos, int &endpos, const int base=10) const; + + /** Returns a double. Implements the i18n strtod. */ + double toDouble( + const int pos, int &endpos ) const; + + /** Returns a long intenger. Implments i18n strtol. */ + static long toLong( + const GUTF8String& src, const int pos, int &endpos, const int base=10); + + static unsigned long toULong( + const GUTF8String& src, const int pos, int &endpos, const int base=10); + + static double toDouble( + const GUTF8String& src, const int pos, int &endpos); + + /** Returns a long intenger. Implments i18n strtol. */ + static long toLong( + const GNativeString& src, const int pos, int &endpos, const int base=10); + + static unsigned long toULong( + const GNativeString& src, const int pos, int &endpos, const int base=10); + + static double toDouble( + const GNativeString& src, const int pos, int &endpos); + + // -- HASHING + + // -- COMPARISONS + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const GBaseString &s2, const int len=(-1)) const; + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const char *s2, const int len=(-1)) const; + /** Returns an #int#. Compares string with #s2# and returns + sorting order. */ + int cmp(const char s2) const; + /** Returns an #int#. Compares #s2# with #s2# and returns + sorting order. */ + static int cmp(const char *s1, const char *s2, const int len=(-1)); + /** Returns a boolean. The Standard C strncmp takes two string and + compares the first N characters. static bool GBaseString::ncmp + will compare #s1# with #s2# with the #len# characters starting + from the beginning of the string. */ + /** String comparison. Returns true if and only if character + strings #s1# and #s2# are equal (as with #strcmp#.) + */ + bool operator==(const GBaseString &s2) const; + bool operator==(const char *s2) const; + friend bool operator==(const char *s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# and #s2# are not equal (as with #strcmp#.) + */ + bool operator!=(const GBaseString &s2) const; + bool operator!=(const char *s2) const; + friend bool operator!=(const char *s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically greater than or equal to + string #s2# (as with #strcmp#.) */ + bool operator>=(const GBaseString &s2) const; + bool operator>=(const char *s2) const; + bool operator>=(const char s2) const; + friend bool operator>=(const char *s1, const GBaseString &s2); + friend bool operator>=(const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically less than string #s2# + (as with #strcmp#.) + */ + bool operator<(const GBaseString &s2) const; + bool operator<(const char *s2) const; + bool operator<(const char s2) const; + friend bool operator<(const char *s1, const GBaseString &s2); + friend bool operator<(const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically greater than string #s2# + (as with #strcmp#.) + */ + bool operator> (const GBaseString &s2) const; + bool operator> (const char *s2) const; + bool operator> (const char s2) const; + friend bool operator> (const char *s1, const GBaseString &s2); + friend bool operator> (const char s1, const GBaseString &s2); + + /** String comparison. Returns true if and only if character + strings #s1# is lexicographically less than or equal to string + #s2# (as with #strcmp#.) + */ + bool operator<=(const GBaseString &s2) const; + bool operator<=(const char *s2) const; + bool operator<=(const char s2) const; + friend bool operator<=(const char *s1, const GBaseString &s2); + friend bool operator<=(const char s1, const GBaseString &s2); + + /** Returns an integer. Implements a functional i18n atoi. Note + that if you pass a GBaseString that is not in Native format + the results may be disparaging. */ + + /** Returns a hash code for the string. This hashing function + helps when creating associative maps with string keys (see + \Ref{GMap}). This hash code may be reduced to an arbitrary + range by computing its remainder modulo the upper bound of + the range. */ + friend unsigned int hash(const GBaseString &ref); + // -- HELPERS + friend class GStringRep; + + /// Returns next non space position. + int nextNonSpace( const int from=0, const int len=(-1) ) const; + + /// Returns next character position. + int nextChar( const int from=0 ) const; + + /// Returns next non space position. + int nextSpace( const int from=0, const int len=(-1) ) const; + + /// return the position after the last non-whitespace character. + int firstEndSpace( const int from=0,const int len=(-1) ) const; + + /// Tests if the string is legally encoded in the current codepage. + bool is_valid(void) const; + + /// copy to a wchar_t buffer + int ncopy(wchar_t * const buf, const int buflen) const; + +protected: + const char *gstr; + static void throw_illegal_subscript() no_return; + static const char *nullstr; +public: + GNativeString UTF8ToNative( + const bool currentlocale=false, + const EscapeMode escape=UNKNOWN_ESCAPED) const; + GUTF8String NativeToUTF8(void) const; +protected: + int CheckSubscript(int n) const; +}; + +/** General purpose character string. + Each instance of class #GUTF8String# represents a character + string. Overloaded operators provide a value semantic to + #GUTF8String# objects. Conversion operators and constructors + transparently convert between #GUTF8String# objects and + #const char*# pointers. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with #GUTF8String# objects since there is a fast conversion + operator from #GUTF8String# to "#const char*#". Functions + returning strings should return #GUTF8String# or #GNativeString# + objects because the class will automatically manage the necessary + memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GUTF8String : public GBaseString +{ +public: + ~GUTF8String(); + void init(void); + + GUTF8String &init(const GP<GStringRep> &rep); + + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GUTF8String(void); + /// Constructs a string from a character. + GUTF8String(const char dat); + /// Constructs a string from a null terminated character array. + GUTF8String(const char *str); + /// Constructs a string from a null terminated character array. + GUTF8String(const unsigned char *str); + GUTF8String(const unsigned short *dat); + GUTF8String(const unsigned long *dat); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GUTF8String(const char *dat, unsigned int len); + GUTF8String(const unsigned short *dat, unsigned int len); + GUTF8String(const unsigned long *dat, unsigned int len); + + /// Construct from base class. + GUTF8String(const GP<GStringRep> &str); + GUTF8String(const GBaseString &str); + GUTF8String(const GUTF8String &str); + GUTF8String(const GNativeString &str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GUTF8String(const GBaseString &gs, int from, int len); + + /** Copy a null terminated character array. Resets this string + with the character string contained in the null terminated + character array #str#. */ + GUTF8String& operator= (const char str); + GUTF8String& operator= (const char *str); + GUTF8String& operator= (const GP<GStringRep> &str); + GUTF8String& operator= (const GBaseString &str); + GUTF8String& operator= (const GUTF8String &str); + GUTF8String& operator= (const GNativeString &str); + + /** Constructs a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String(const GUTF8String &fmt, va_list &args); + + /// Constructs a string from a character. + /** Constructs a string with a human-readable representation of + integer #number#. The format is similar to format #"%d"# in + function #printf#. */ + GUTF8String(const int number); + + /** Constructs a string with a human-readable representation of + floating point number #number#. The format is similar to + format #"%f"# in function #printf#. */ + GUTF8String(const double number); + + + /** Initializes a string with a formatted string (as in #printf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #printf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String &format(const char *fmt, ... ); + /** Initializes a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer + than 32768 characters. */ + GUTF8String &vformat(const GUTF8String &fmt, va_list &args); + + /** Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GUTF8String toEscaped( const bool tosevenbit=false ) const; + + /** Converts strings containing HTML/XML escaped characters into + their unescaped forms. Numeric representations of characters + (e.g., "&" or "&" for "*") are the only forms + converted by this function. */ + GUTF8String fromEscaped( void ) const; + + /** Converts strings containing HTML/XML escaped characters + (e.g., "<" for "<") into their unescaped forms. The + conversion is partially defined by the ConvMap argument which + specifies the conversion strings to be recognized. Numeric + representations of characters (e.g., "&" or "&" + for "*") are always converted. */ + GUTF8String fromEscaped( + const GMap<GUTF8String,GUTF8String> ConvMap ) const; + + + // -- CONCATENATION + /// Appends character #ch# to the string. + GUTF8String& operator+= (char ch); + + /// Appends the null terminated character array #str# to the string. + GUTF8String& operator+= (const char *str); + /// Appends the specified GBaseString to the string. + GUTF8String& operator+= (const GBaseString &str); + + /** Returns a sub-string. The sub-string is composed by copying + #len# characters starting at position #from# in this string. + The length of the resulting string may be smaller than #len# + if the specified range is too large. */ + GUTF8String substr(int from, int len/*=(-1)*/) const; + + /** Returns an upper case copy of this string. The returned string + contains a copy of the current string with all letters turned + into upper case letters. */ + GUTF8String upcase( void ) const; + /** Returns an lower case copy of this string. The returned string + contains a copy of the current string with all letters turned + into lower case letters. */ + GUTF8String downcase( void ) const; + + /** Concatenates strings. Returns a string composed by concatenating + the characters of strings #s1# and #s2#. + */ + GUTF8String operator+(const GBaseString &s2) const; + GUTF8String operator+(const GUTF8String &s2) const; + GUTF8String operator+(const GNativeString &s2) const; + GUTF8String operator+(const char *s2) const; + friend GUTF8String operator+(const char *s1, const GUTF8String &s2); + + /** Provides a direct access to the string buffer. Returns a + pointer for directly accessing the string buffer. This pointer + valid remains valid as long as the string is not modified by + other means. Positive values for argument #n# represent the + length of the returned buffer. The returned string buffer will + be large enough to hold at least #n# characters plus a null + character. If #n# is positive but smaller than the string + length, the string will be truncated to #n# characters. */ + char *getbuf(int n = -1); + /** Set the character at position #n# to value #ch#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len#, where #len# is the length of the string. If character + #ch# is zero, the string is truncated at position #n#. The + first character of a string is numbered zero. Negative + positions represent characters relative to the end of the + string. If position #n# is equal to the length of the string, + this function appends character #ch# to the end of the string. */ + void setat(const int n, const char ch); +public: + typedef enum GStringRep::EncodeType EncodeType; + static GUTF8String create(void const * const buf, + const unsigned int size, + const EncodeType encodetype, const GUTF8String &encoding); + static GUTF8String create( void const * const buf, + unsigned int size, const EncodeType encodetype ); + static GUTF8String create( void const * const buf, + const unsigned int size, const GUTF8String &encoding ); + static GUTF8String create( void const * const buf, + const unsigned int size, const GP<GStringRep::Unicode> &remainder); + GP<GStringRep::Unicode> get_remainder(void) const; + static GUTF8String create( const char *buf, const unsigned int bufsize ); + static GUTF8String create( const unsigned short *buf, const unsigned int bufsize ); + static GUTF8String create( const unsigned long *buf, const unsigned int bufsize ); +}; + + +#if !HAS_WCHAR +#define GBaseString GUTF8String +#endif + +/** General purpose character string. + Each instance of class #GNativeString# represents a character + string. Overloaded operators provide a value semantic to + #GNativeString# objects. Conversion operators and constructors + transparently convert between #GNativeString# objects and + #const char*# pointers. + + Functions taking strings as arguments should declare their + arguments as "#const char*#". Such functions will work equally + well with #GNativeString# objects since there is a fast conversion + operator from #GNativeString# to "#const char*#". Functions + returning strings should return #GUTF8String# or #GNativeString# + objects because the class will automatically manage the necessary + memory. + + Characters in the string can be identified by their position. The + first character of a string is numbered zero. Negative positions + represent characters relative to the end of the string (i.e. + position #-1# accesses the last character of the string, + position #-2# represents the second last character, etc.) */ + +class GNativeString : public GBaseString +{ +public: + ~GNativeString(); + // -- CONSTRUCTORS + /** Null constructor. Constructs an empty string. */ + GNativeString(void); + /// Constructs a string from a character. + GNativeString(const char dat); + /// Constructs a string from a null terminated character array. + GNativeString(const char *str); + /// Constructs a string from a null terminated character array. + GNativeString(const unsigned char *str); + GNativeString(const unsigned short *str); + GNativeString(const unsigned long *str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GNativeString(const char *dat, unsigned int len); + GNativeString(const unsigned short *dat, unsigned int len); + GNativeString(const unsigned long *dat, unsigned int len); + /// Construct from base class. + GNativeString(const GP<GStringRep> &str); + GNativeString(const GBaseString &str); +#if HAS_WCHAR + GNativeString(const GUTF8String &str); +#endif + GNativeString(const GNativeString &str); + /** Constructs a string from a character array. Elements of the + character array #dat# are added into the string until the + string length reaches #len# or until encountering a null + character (whichever comes first). */ + GNativeString(const GBaseString &gs, int from, int len); + + /** Constructs a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString(const GNativeString &fmt, va_list &args); + + /** Constructs a string with a human-readable representation of + integer #number#. The format is similar to format #"%d"# in + function #printf#. */ + GNativeString(const int number); + + /** Constructs a string with a human-readable representation of + floating point number #number#. The format is similar to + format #"%f"# in function #printf#. */ + GNativeString(const double number); + +#if !HAS_WCHAR +#undef GBaseString +#else + /// Initialize this string class + void init(void); + + /// Initialize this string class + GNativeString &init(const GP<GStringRep> &rep); + + /** Copy a null terminated character array. Resets this string with + the character string contained in the null terminated character + array #str#. */ + GNativeString& operator= (const char str); + GNativeString& operator= (const char *str); + GNativeString& operator= (const GP<GStringRep> &str); + GNativeString& operator= (const GBaseString &str); + GNativeString& operator= (const GUTF8String &str); + GNativeString& operator= (const GNativeString &str); + // -- CONCATENATION + /// Appends character #ch# to the string. + GNativeString& operator+= (char ch); + /// Appends the null terminated character array #str# to the string. + GNativeString& operator+= (const char *str); + /// Appends the specified GBaseString to the string. + GNativeString& operator+= (const GBaseString &str); + + /** Returns a sub-string. The sub-string is composed by copying + #len# characters starting at position #from# in this string. + The length of the resulting string may be smaller than #len# + if the specified range is too large. */ + GNativeString substr(int from, int len/*=(-1)*/) const; + + /** Returns an upper case copy of this string. The returned + string contains a copy of the current string with all letters + turned into upper case letters. */ + GNativeString upcase( void ) const; + /** Returns an lower case copy of this string. The returned + string contains a copy of the current string with all letters + turned into lower case letters. */ + GNativeString downcase( void ) const; + + + GNativeString operator+(const GBaseString &s2) const; + GNativeString operator+(const GNativeString &s2) const; + GUTF8String operator+(const GUTF8String &s2) const; + GNativeString operator+(const char *s2) const; + friend GNativeString operator+(const char *s1, const GNativeString &s2); + + /** Initializes a string with a formatted string (as in #printf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #printf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString &format(const char *fmt, ... ); + /** Initializes a string with a formatted string (as in #vprintf#). + The string is re-initialized with the characters generated + according to the specified format #fmt# and using the optional + arguments. See the ANSI-C function #vprintf()# for more + information. The current implementation will cause a + segmentation violation if the resulting string is longer than + 32768 characters. */ + GNativeString &vformat(const GNativeString &fmt, va_list &args); + + /** Returns a copy of this string with characters used in XML with + '<' to "<", '>' to ">", '&' to "&" '\'' to + "'", and '\"' to """. Characters 0x01 through + 0x1f are also escaped. */ + GNativeString toEscaped( const bool tosevenbit=false ) const; + + + /** Provides a direct access to the string buffer. Returns a + pointer for directly accessing the string buffer. This + pointer valid remains valid as long as the string is not + modified by other means. Positive values for argument #n# + represent the length of the returned buffer. The returned + string buffer will be large enough to hold at least #n# + characters plus a null character. If #n# is positive but + smaller than the string length, the string will be truncated + to #n# characters. */ + char *getbuf(int n = -1); + /** Set the character at position #n# to value #ch#. An exception + \Ref{GException} is thrown if number #n# is not in range #-len# + to #len#, where #len# is the length of the string. If + character #ch# is zero, the string is truncated at position + #n#. The first character of a string is numbered zero. + Negative positions represent characters relative to the end of + the string. If position #n# is equal to the length of the + string, this function appends character #ch# to the end of the + string. */ + void setat(const int n, const char ch); + + static GNativeString create( const char *buf, const unsigned int bufsize ); + static GNativeString create( const unsigned short *buf, const unsigned int bufsize ); + static GNativeString create( const unsigned long *buf, const unsigned int bufsize ); +#endif // WinCE +}; + +//@} + +inline +GBaseString::operator const char* ( void ) const +{ + return ptr?(*this)->data:nullstr; +} + +inline unsigned int +GBaseString::length( void ) const +{ + return ptr ? (*this)->size : 0; +} + +inline bool +GBaseString::operator! ( void ) const +{ + return !ptr; +} + +inline GUTF8String +GUTF8String::upcase( void ) const +{ + if (ptr) return (*this)->upcase(); + return *this; +} + +inline GUTF8String +GUTF8String::downcase( void ) const +{ + if (ptr) return (*this)->downcase(); + return *this; +} + +inline void +GUTF8String::init(void) +{ GBaseString::init(); } + +inline GUTF8String & +GUTF8String::init(const GP<GStringRep> &rep) +{ GP<GStringRep>::operator=(rep?rep->toUTF8(true):rep); init(); return *this; } + +inline GUTF8String & +GUTF8String::vformat(const GUTF8String &fmt, va_list &args) +{ return (*this = (fmt.ptr?GUTF8String(fmt,args):fmt)); } + +inline GUTF8String +GUTF8String::toEscaped( const bool tosevenbit ) const +{ return ptr?GUTF8String((*this)->toEscaped(tosevenbit)):(*this); } + +inline GP<GStringRep::Unicode> +GUTF8String::get_remainder(void) const +{ + GP<GStringRep::Unicode> retval; + if(ptr) + retval=((*this)->get_remainder()); + return retval; +} + +inline +GUTF8String::GUTF8String(const GNativeString &str) +{ init(str.length()?(str->toUTF8(true)):(GP<GStringRep>)str); } + +inline +GUTF8String::GUTF8String(const GP<GStringRep> &str) +{ init(str?(str->toUTF8(true)):str); } + +inline +GUTF8String::GUTF8String(const GBaseString &str) +{ init(str.length()?(str->toUTF8(true)):(GP<GStringRep>)str); } + +inline void +GBaseString::init(void) +{ + gstr=ptr?((*this)->data):nullstr; +} +/** Returns an integer. Implements i18n atoi. */ +inline int +GBaseString::toInt(void) const +{ return ptr?(*this)->toInt():0; } + +/** Returns a long intenger. Implments i18n strtol. */ +inline long +GBaseString::toLong(const int pos, int &endpos, const int base) const +{ + long int retval=0; + if(ptr) + { + retval=(*this)->toLong(pos, endpos, base); + }else + { + endpos=(-1); + } + return retval; +} + +inline long +GBaseString::toLong( + const GUTF8String& src, const int pos, int &endpos, const int base) +{ + return src.toLong(pos,endpos,base); +} + +inline long +GBaseString::toLong( + const GNativeString& src, const int pos, int &endpos, const int base) +{ + return src.toLong(pos,endpos,base); +} + +/** Returns a unsigned long integer. Implements i18n strtoul. */ +inline unsigned long +GBaseString::toULong(const int pos, int &endpos, const int base) const +{ + unsigned long retval=0; + if(ptr) + { + retval=(*this)->toULong(pos, endpos, base); + }else + { + endpos=(-1); + } + return retval; +} + +inline unsigned long +GBaseString::toULong( + const GUTF8String& src, const int pos, int &endpos, const int base) +{ + return src.toULong(pos,endpos,base); +} + +inline unsigned long +GBaseString::toULong( + const GNativeString& src, const int pos, int &endpos, const int base) +{ + return src.toULong(pos,endpos,base); +} + +/** Returns a double. Implements the i18n strtod. */ +inline double +GBaseString::toDouble( + const int pos, int &endpos ) const +{ + double retval=(double)0; + if(ptr) + { + retval=(*this)->toDouble(pos, endpos); + }else + { + endpos=(-1); + } + return retval; +} + +inline double +GBaseString::toDouble( + const GUTF8String& src, const int pos, int &endpos) +{ + return src.toDouble(pos,endpos); +} + +inline double +GBaseString::toDouble( + const GNativeString& src, const int pos, int &endpos) +{ + return src.toDouble(pos,endpos); +} + +inline GBaseString & +GBaseString::init(const GP<GStringRep> &rep) +{ GP<GStringRep>::operator=(rep); init(); return *this;} + +inline char +GBaseString::operator[] (int n) const +{ return ((n||ptr)?((*this)->data[CheckSubscript(n)]):0); } + +inline int +GBaseString::search(char c, int from) const +{ return ptr?((*this)->search(c,from)):(-1); } + +inline int +GBaseString::search(const char *str, int from) const +{ return ptr?((*this)->search(str,from)):(-1); } + +inline int +GBaseString::rsearch(char c, const int from) const +{ return ptr?((*this)->rsearch(c,from)):(-1); } + +inline int +GBaseString::rsearch(const char *str, const int from) const +{ return ptr?((*this)->rsearch(str,from)):(-1); } + +inline int +GBaseString::contains(const char accept[], const int from) const +{ return ptr?((*this)->contains(accept,from)):(-1); } + +inline int +GBaseString::rcontains(const char accept[], const int from) const +{ return ptr?((*this)->rcontains(accept,from)):(-1); } + +inline int +GBaseString::cmp(const GBaseString &s2, const int len) const +{ return GStringRep::cmp(*this,s2,len); } + +inline int +GBaseString::cmp(const char *s2, const int len) const +{ return GStringRep::cmp(*this,s2,len); } + +inline int +GBaseString::cmp(const char s2) const +{ return GStringRep::cmp(*this,&s2,1); } + +inline int +GBaseString::cmp(const char *s1, const char *s2, const int len) +{ return GStringRep::cmp(s1,s2,len); } + +inline bool +GBaseString::operator==(const GBaseString &s2) const +{ return !cmp(s2); } + +inline bool +GBaseString::operator==(const char *s2) const +{ return !cmp(s2); } + +inline bool +GBaseString::operator!=(const GBaseString &s2) const +{ return !!cmp(s2); } + +inline bool +GBaseString::operator!=(const char *s2) const +{ return !!cmp(s2); } + +inline bool +GBaseString::operator>=(const GBaseString &s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator>=(const char *s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator>=(const char s2) const +{ return (cmp(s2)>=0); } + +inline bool +GBaseString::operator<(const GBaseString &s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator<(const char *s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator<(const char s2) const +{ return (cmp(s2)<0); } + +inline bool +GBaseString::operator> (const GBaseString &s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator> (const char *s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator> (const char s2) const +{ return (cmp(s2)>0); } + +inline bool +GBaseString::operator<=(const GBaseString &s2) const +{ return (cmp(s2)<=0); } + +inline bool +GBaseString::operator<=(const char *s2) const +{ return (cmp(s2)<=0); } + +inline bool +GBaseString::operator<=(const char s2) const +{ return (cmp(s2)<=0); } + +inline int +GBaseString::nextNonSpace( const int from, const int len ) const +{ return ptr?(*this)->nextNonSpace(from,len):0; } + +inline int +GBaseString::nextChar( const int from ) const +{ return ptr?(*this)->nextChar(from):0; } + +inline int +GBaseString::nextSpace( const int from, const int len ) const +{ return ptr?(*this)->nextSpace(from,len):0; } + +inline int +GBaseString::firstEndSpace( const int from,const int len ) const +{ return ptr?(*this)->firstEndSpace(from,len):0; } + +inline bool +GBaseString::is_valid(void) const +{ return ptr?((*this)->is_valid()):true; } + +inline int +GBaseString::ncopy(wchar_t * const buf, const int buflen) const +{if(buf&&buflen)buf[0]=0;return ptr?((*this)->ncopy(buf,buflen)):0;} + +inline int +GBaseString::CheckSubscript(int n) const +{ + if(n) + { + if (n<0 && ptr) + n += (*this)->size; + if (n<0 || !ptr || n > (int)(*this)->size) + throw_illegal_subscript(); + } + return n; +} + +inline GBaseString::GBaseString(void) { init(); } + +inline GUTF8String::GUTF8String(void) { } + +inline GUTF8String::GUTF8String(const GUTF8String &str) : GBaseString(str) +{ init(str); } + +inline GUTF8String& GUTF8String::operator= (const GP<GStringRep> &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GBaseString &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GUTF8String &str) +{ return init(str); } + +inline GUTF8String& GUTF8String::operator= (const GNativeString &str) +{ return init(str); } + +inline GUTF8String +GUTF8String::create( const char *buf, const unsigned int bufsize ) +{ +#if HAS_WCHAR + return GNativeString(buf,bufsize); +#else + return GUTF8String(buf,bufsize); +#endif +} + +inline GUTF8String +GUTF8String::create( const unsigned short *buf, const unsigned int bufsize ) +{ + return GUTF8String(buf,bufsize); +} + +inline GUTF8String +GUTF8String::create( const unsigned long *buf, const unsigned int bufsize ) +{ + return GUTF8String(buf,bufsize); +} + +inline GNativeString::GNativeString(void) {} + +#if !HAS_WCHAR +// For Windows CE, GNativeString is essentially GUTF8String + +inline +GNativeString::GNativeString(const GUTF8String &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const GP<GStringRep> &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const char dat) +: GUTF8String(dat) {} + +inline +GNativeString::GNativeString(const char *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned char *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned short *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const unsigned long *str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const char *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const unsigned short *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const unsigned long *dat, unsigned int len) +: GUTF8String(dat,len) {} + +inline +GNativeString::GNativeString(const GNativeString &str) +: GUTF8String(str) {} + +inline +GNativeString::GNativeString(const int number) +: GUTF8String(number) {} + +inline +GNativeString::GNativeString(const double number) +: GUTF8String(number) {} + +inline +GNativeString::GNativeString(const GNativeString &fmt, va_list &args) +: GUTF8String(fmt,args) {} + +#else // HAS_WCHAR + +/// Initialize this string class +inline void +GNativeString::init(void) +{ GBaseString::init(); } + +/// Initialize this string class +inline GNativeString & +GNativeString::init(const GP<GStringRep> &rep) +{ + GP<GStringRep>::operator=(rep?rep->toNative(GStringRep::NOT_ESCAPED):rep); + init(); + return *this; +} + +inline GNativeString +GNativeString::substr(int from, int len) const +{ return GNativeString(*this, from, len); } + +inline GNativeString & +GNativeString::vformat(const GNativeString &fmt, va_list &args) +{ return (*this = (fmt.ptr?GNativeString(fmt,args):fmt)); } + +inline GNativeString +GNativeString::toEscaped( const bool tosevenbit ) const +{ return ptr?GNativeString((*this)->toEscaped(tosevenbit)):(*this); } + +inline +GNativeString::GNativeString(const GUTF8String &str) +{ + if (str.length()) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init((GP<GStringRep>)str); +} + +inline +GNativeString::GNativeString(const GP<GStringRep> &str) +{ + if (str) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init(str); +} + +inline +GNativeString::GNativeString(const GBaseString &str) +{ + if (str.length()) + init(str->toNative(GStringRep::NOT_ESCAPED)); + else + init((GP<GStringRep>)str); +} + + +inline +GNativeString::GNativeString(const GNativeString &fmt, va_list &args) +{ + if (fmt.ptr) + init(fmt->vformat(args)); + else + init(fmt); +} + +inline GNativeString +GNativeString::create( const char *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString +GNativeString::create( const unsigned short *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString +GNativeString::create( const unsigned long *buf, const unsigned int bufsize ) +{ + return GNativeString(buf,bufsize); +} + +inline GNativeString& +GNativeString::operator= (const GP<GStringRep> &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GBaseString &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GUTF8String &str) +{ return init(str); } + +inline GNativeString& +GNativeString::operator= (const GNativeString &str) +{ return init(str); } + +inline GNativeString +GNativeString::upcase( void ) const +{ + if (ptr) return (*this)->upcase(); + return *this; +} + +inline GNativeString +GNativeString::downcase( void ) const +{ + if (ptr) return (*this)->downcase(); + return *this; +} + +#endif // HAS_WCHAR + +inline bool +operator==(const char *s1, const GBaseString &s2) +{ return !s2.cmp(s1); } + +inline bool +operator!=(const char *s1, const GBaseString &s2) +{ return !!s2.cmp(s1); } + +inline bool +operator>=(const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)<=0); } + +inline bool +operator>=(const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)<=0); } + +inline bool +operator<(const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)>0); } + +inline bool +operator<(const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)>0); } + +inline bool +operator> (const char *s1, const GBaseString &s2) +{ return (s2.cmp(s1)<0); } + +inline bool +operator> (const char s1, const GBaseString &s2) +{ return (s2.cmp(s1)<0); } + +inline bool +operator<=(const char *s1, const GBaseString &s2) +{ return !(s1>s2); } + +inline bool +operator<=(const char s1, const GBaseString &s2) +{ return !(s1>s2); } + +// ------------------- The end + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/GThreads.cpp b/kviewshell/plugins/djvu/libdjvu/GThreads.cpp new file mode 100644 index 00000000..ce88361e --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GThreads.cpp @@ -0,0 +1,1887 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GThreads.cpp,v 1.15 2004/04/21 14:54:43 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// This file defines machine independent classes +// for running and synchronizing threads. +// - Author: Leon Bottou, 01/1998 + +// From: Leon Bottou, 1/31/2002 +// Almost unchanged by Lizardtech. +// GSafeFlags should go because it not as safe as it claims. + +#include "GThreads.h" +#include "GException.h" +#include "DjVuMessageLite.h" +#include <stdlib.h> +#include <stdio.h> + +// ---------------------------------------- +// Consistency check + +#if THREADMODEL!=NOTHREADS +#ifdef USE_EXCEPTION_EMULATION +#warning "Compiler must support thread safe exceptions" +#endif //USE_EXCEPTION_EMULATION +#if defined(__GNUC__) +#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=8)) +#warning "GCC 2.8 exceptions are not thread safe." +#warning "Use properly configured EGCS-1.1 or greater." +#endif // (__GNUC__<2 ... +#endif // defined(__GNUC__) +#endif // THREADMODEL!=NOTHREADS + +#ifndef _DEBUG +#if defined(DEBUG) +#define _DEBUG /* */ +#elif DEBUGLVL >= 1 +#define _DEBUG /* */ +#endif +#endif + +#if THREADMODEL==WINTHREADS +# include <process.h> +#endif +#if THREADMODEL==COTHREADS +# include <setjmp.h> +# include <string.h> +# include <unistd.h> +# include <sys/types.h> +# include <sys/time.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// NOTHREADS +// ---------------------------------------- + +#if THREADMODEL==NOTHREADS +int +GThread::create( void (*entry)(void*), void *arg) +{ + (*entry)(arg); + return 0; +} +#endif + + +// ---------------------------------------- +// WIN32 IMPLEMENTATION +// ---------------------------------------- + +#if THREADMODEL==WINTHREADS + +static unsigned __stdcall +start(void *arg) +{ + GThread *gt = (GThread*)arg; + try + { + G_TRY + { + gt->xentry( gt->xarg ); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } + return 0; +} + +GThread::GThread(int stacksize) + : hthr(0), thrid(0), xentry(0), xarg(0) +{ +} + +GThread::~GThread() +{ + if (hthr) + CloseHandle(hthr); + hthr = 0; + thrid = 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (hthr) + return -1; + xentry = entry; + xarg = arg; + unsigned uthread = 0; + hthr = (HANDLE)_beginthreadex(NULL, 0, start, (void*)this, 0, &uthread); + thrid = (DWORD) uthread; + if (hthr) + return 0; + return -1; +} + +void +GThread::terminate() +{ + OutputDebugString("Terminating thread.\n"); + if (hthr) + TerminateThread(hthr,0); +} + +int +GThread::yield() +{ + Sleep(0); + return 0; +} + +void * +GThread::current() +{ + return (void*) GetCurrentThreadId(); +} + +struct thr_waiting { + struct thr_waiting *next; + struct thr_waiting *prev; + BOOL waiting; + HANDLE gwait; +}; + +GMonitor::GMonitor() + : ok(0), count(1), head(0), tail(0) +{ + InitializeCriticalSection(&cs); + locker = GetCurrentThreadId(); + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + EnterCriticalSection(&cs); + for (struct thr_waiting *w=head; w; w=w->next) + SetEvent(w->gwait); + LeaveCriticalSection(&cs); + DeleteCriticalSection(&cs); +} + +void +GMonitor::enter() +{ + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + { + if (ok) + EnterCriticalSection(&cs); + locker = self; + count = 1; + } + count -= 1; +} + +void +GMonitor::leave() +{ + DWORD self = GetCurrentThreadId(); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + count += 1; + if (count > 0) + { + count = 1; + if (ok) + LeaveCriticalSection(&cs); + } +} + +void +GMonitor::signal() +{ + if (ok) + { + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + for (struct thr_waiting *w=head; w; w=w->next) + if (w->waiting) + { + SetEvent(w->gwait); + w->waiting = FALSE; + break; // Only one thread is allowed to run! + } + } +} + +void +GMonitor::broadcast() +{ + if (ok) + { + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + for (struct thr_waiting *w=head; w; w=w->next) + if (w->waiting) + { + SetEvent(w->gwait); + w->waiting = FALSE; + } + } +} + +void +GMonitor::wait() +{ + // Check state + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Prepare wait record + struct thr_waiting waitrec; + waitrec.waiting = TRUE; + waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL); + waitrec.next = 0; + waitrec.prev = tail; + // Link wait record (protected by critical section) + *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; + *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec; + // Start wait + int sav_count = count; + count = 1; + LeaveCriticalSection(&cs); + WaitForSingleObject(waitrec.gwait,INFINITE); + // Re-acquire + EnterCriticalSection(&cs); + count = sav_count; + locker = self; + // Unlink wait record + *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev; + *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next; + CloseHandle(waitrec.gwait); + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check state + DWORD self = GetCurrentThreadId(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Prepare wait record + struct thr_waiting waitrec; + waitrec.waiting = TRUE; + waitrec.gwait = CreateEvent(NULL,FALSE,FALSE,NULL); + waitrec.next = 0; + waitrec.prev = tail; + // Link wait record (protected by critical section) + *(waitrec.prev ? &waitrec.prev->next : &head) = &waitrec; + *(waitrec.next ? &waitrec.next->prev : &tail) = &waitrec; + // Start wait + int sav_count = count; + count = 1; + LeaveCriticalSection(&cs); + WaitForSingleObject(waitrec.gwait,timeout); + // Re-acquire + EnterCriticalSection(&cs); + count = sav_count; + locker = self; + // Unlink wait record + *(waitrec.next ? &waitrec.next->prev : &tail) = waitrec.prev; + *(waitrec.prev ? &waitrec.prev->next : &head) = waitrec.next; + CloseHandle(waitrec.gwait); + } +} + +#endif + + + +// ---------------------------------------- +// MACTHREADS IMPLEMENTATION (from Praveen) +// ---------------------------------------- + +#if THREADMODEL==MACTHREADS + +// Doubly linked list of waiting threads +struct thr_waiting { + struct thr_waiting *next; // ptr to next waiting thread record + struct thr_waiting *prev; // ptr to ptr to this waiting thread + unsigned long thid; // id of waiting thread + int *wchan; // cause of the wait +}; +static struct thr_waiting *first_waiting_thr = 0; +static struct thr_waiting *last_waiting_thr = 0; + + +// Stops current thread. +// Argument ``self'' must be current thread id. +// Assumes ``ThreadBeginCritical'' has been called before. +static void +macthread_wait(ThreadID self, int *wchan) +{ + // Prepare and link wait record + struct thr_waiting wait; // no need to malloc :-) + wait.thid = self; + wait.wchan = wchan; + wait.next = 0; + wait.prev = last_waiting_thr; + *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = &wait; + *(wait.next ? &wait.next->prev : &last_waiting_thr ) = &wait; + // Leave critical section and start waiting. + (*wchan)++; + SetThreadStateEndCritical(self, kStoppedThreadState, kNoThreadID); + // The Apple documentation says that the above call reschedules a new + // thread. Therefore it will only return when the thread wakes up. + ThreadBeginCritical(); + (*wchan)--; + // Unlink wait record + *(wait.prev ? &wait.prev->next : &first_waiting_thr ) = wait.next; + *(wait.next ? &wait.next->prev : &last_waiting_thr ) = wait.prev; + // Returns from the wait. +} + +// Wakeup one thread or all threads waiting on cause wchan +static void +macthread_wakeup(int *wchan, int onlyone) +{ + if (*wchan == 0) + return; + for (struct thr_waiting *q=first_waiting_thr; q; q=q->next) + if (q->wchan == wchan) { + // Found a waiting thread + q->wchan = 0; + SetThreadState(q->thid, kReadyThreadState, kNoThreadID); + if (onlyone) + return; + } +} + +GThread::GThread(int stacksize) + : thid(kNoThreadID), xentry(0), xarg(0) +{ +} + +GThread::~GThread(void) +{ + thid = kNoThreadID; +} + +pascal void * +GThread::start(void *arg) +{ + GThread *gt = (GThread*)arg; + try + { + G_TRY + { + (gt->xentry)(gt->xarg); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } + return 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (xentry || thid!=kNoThreadID) + return -1; + xentry = entry; + xarg = arg; + int err = NewThread( kCooperativeThread, GThread::start , this, 0, + kCreateIfNeeded, (void**)nil, &thid ); + if( err != noErr ) + return err; + return 0; +} + +void +GThread::terminate() +{ + if (thid != kNoThreadID) { + DisposeThread( thid, NULL, false ); + thid = kNoThreadID; + } +} + +int +GThread::yield() +{ + YieldToAnyThread(); + return 0; +} + +void* +GThread::current() +{ + unsigned long thid = kNoThreadID; + GetCurrentThread(&thid); + return (void*) thid; +} + + +// GMonitor implementation +GMonitor::GMonitor() + : ok(0), count(1), locker(0), wlock(0), wsig(0) +{ + locker = kNoThreadID; + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + ThreadBeginCritical(); + macthread_wakeup(&wsig, 0); + macthread_wakeup(&wlock, 0); + ThreadEndCritical(); + YieldToAnyThread(); +} + +void +GMonitor::enter() +{ + ThreadID self; + GetCurrentThread(&self); + ThreadBeginCritical(); + if (count>0 || self!=locker) + { + while (ok && count<=0) + macthread_wait(self, &wlock); + count = 1; + locker = self; + } + count -= 1; + ThreadEndCritical(); +} + +void +GMonitor::leave() +{ + ThreadID self; + GetCurrentThread(&self); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_leave") ); + ThreadBeginCritical(); + if (++count > 0) + macthread_wakeup(&wlock, 1); + ThreadEndCritical(); +} + +void +GMonitor::signal() +{ + ThreadID self; + GetCurrentThread(&self); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + ThreadBeginCritical(); + macthread_wakeup(&wsig, 1); + ThreadEndCritical(); +} + +void +GMonitor::broadcast() +{ + ThreadID self; + GetCurrentThread(&self); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + ThreadBeginCritical(); + macthread_wakeup(&wsig, 0); + ThreadEndCritical(); +} + +void +GMonitor::wait() +{ + // Check state + ThreadID self; + GetCurrentThread(&self); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + ThreadBeginCritical(); + int sav_count = count; + count = 1; + macthread_wakeup(&wlock, 1); + macthread_wait(self, &wsig); + // Re-acquire + while (ok && count<=0) + macthread_wait(self, &wlock); + count = sav_count; + locker = self; + ThreadEndCritical(); + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Timeouts are not used for anything important. + // Just ignore the timeout and wait the regular way. + wait(); +} + +#endif + + + +// ---------------------------------------- +// POSIXTHREADS IMPLEMENTATION +// ---------------------------------------- + +#if THREADMODEL==POSIXTHREADS + +#if defined(CMA_INCLUDE) +#define DCETHREADS +#define pthread_key_create pthread_keycreate +#else +#define pthread_mutexattr_default NULL +#define pthread_condattr_default NULL +#endif + + +void * +GThread::start(void *arg) +{ + GThread *gt = (GThread*)arg; +#ifdef DCETHREADS +#ifdef CANCEL_ON + pthread_setcancel(CANCEL_ON); + pthread_setasynccancel(CANCEL_ON); +#endif +#else // !DCETHREADS +#ifdef PTHREAD_CANCEL_ENABLE + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); +#endif +#ifdef PTHREAD_CANCEL_ASYNCHRONOUS + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); +#endif +#endif + // Catch exceptions +#ifdef __EXCEPTIONS + try + { +#endif + G_TRY + { + (gt->xentry)(gt->xarg); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; +#ifdef __EXCEPTIONS + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } +#endif + return 0; +} + + +// GThread + +GThread::GThread(int stacksize) : + hthr(0), xentry(0), xarg(0) +{ +} + +GThread::~GThread() +{ + hthr = 0; +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (xentry || xarg) + return -1; + xentry = entry; + xarg = arg; +#ifdef DCETHREADS + int ret = pthread_create(&hthr, pthread_attr_default, GThread::start, (void*)this); + if (ret >= 0) + pthread_detach(hthr); +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + int ret = pthread_create(&hthr, &attr, start, (void*)this); + pthread_attr_destroy(&attr); +#endif + return ret; +} + +void +GThread::terminate() +{ + if (xentry || xarg) + pthread_cancel(hthr); +} + +int +GThread::yield() +{ +#ifdef DCETHREADS + pthread_yield(); +#else + // should use sched_yield() when available. + static struct timeval timeout = { 0, 0 }; + ::select(0, 0,0,0, &timeout); +#endif + return 0; +} + +void* +GThread::current() +{ + pthread_t self = pthread_self(); +#if defined(pthread_getunique_np) + return (void*) pthread_getunique_np( & self ); +#elif defined(cma_thread_get_unique) + return (void*) cma_thread_get_unique( & self ); +#else + return (void*) self; +#endif +} + +// -- GMonitor + +GMonitor::GMonitor() + : ok(0), count(1), locker(0) +{ + // none of this should be necessary ... in theory. +#ifdef PTHREAD_MUTEX_INITIALIZER + static pthread_mutex_t tmutex=PTHREAD_MUTEX_INITIALIZER; + memcpy(&mutex,&tmutex,sizeof(mutex)); +#endif +#ifdef PTHREAD_COND_INITIALIZER + static pthread_cond_t tcond=PTHREAD_COND_INITIALIZER; + memcpy(&cond,&tcond,sizeof(cond)); +#endif + // standard + pthread_mutex_init(&mutex, pthread_mutexattr_default); + pthread_cond_init(&cond, pthread_condattr_default); + locker = pthread_self(); + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + + +void +GMonitor::enter() +{ + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + { + if (ok) + pthread_mutex_lock(&mutex); + locker = self; + count = 1; + } + count -= 1; +} + +void +GMonitor::leave() +{ + pthread_t self = pthread_self(); + if (ok && (count>0 || !pthread_equal(locker, self))) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + count += 1; + if (count > 0) + { + count = 1; + if (ok) + pthread_mutex_unlock(&mutex); + } +} + +void +GMonitor::signal() +{ + if (ok) + { + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + pthread_cond_signal(&cond); + } +} + +void +GMonitor::broadcast() +{ + if (ok) + { + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + pthread_cond_broadcast(&cond); + } +} + +void +GMonitor::wait() +{ + // Check + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Release + int sav_count = count; + count = 1; + // Wait + pthread_cond_wait(&cond, &mutex); + // Re-acquire + count = sav_count; + locker = self; + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check + pthread_t self = pthread_self(); + if (count>0 || !pthread_equal(locker, self)) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Release + int sav_count = count; + count = 1; + // Wait + struct timeval abstv; + struct timespec absts; + gettimeofday(&abstv, NULL); // grrr + absts.tv_sec = abstv.tv_sec + timeout/1000; + absts.tv_nsec = abstv.tv_usec*1000 + (timeout%1000)*1000000; + if (absts.tv_nsec > 1000000000) { + absts.tv_nsec -= 1000000000; + absts.tv_sec += 1; + } + pthread_cond_timedwait(&cond, &mutex, &absts); + // Re-acquire + count = sav_count; + locker = self; + } +} + +#endif + + + +// ---------------------------------------- +// CUSTOM COOPERATIVE THREADS +// ---------------------------------------- + +#if THREADMODEL==COTHREADS + +#ifndef __GNUG__ +#error "COTHREADS require G++" +#endif +#if (__GNUC__<2) || ((__GNUC__==2) && (__GNUC_MINOR__<=90)) +#warning "COTHREADS require EGCS-1.1.1 with Leon's libgcc patch." +#warning "You may have trouble with thread-unsafe exceptions..." +#define NO_LIBGCC_HOOKS +#endif + +// -------------------------------------- constants + +// Minimal stack size +#define MINSTACK (32*1024) +// Default stack size +#define DEFSTACK (127*1024) +// Maxtime between checking fdesc (ms) +#define MAXFDWAIT (200) +// Maximum time to wait in any case +#define MAXWAIT (60*60*1000) +// Maximum penalty for hog task (ms) +#define MAXPENALTY (1000) +// Trace task switches +#undef COTHREAD_TRACE +#undef COTHREAD_TRACE_VERBOSE + +// -------------------------------------- context switch code + +struct mach_state { + jmp_buf buf; +}; + +static void +mach_switch(mach_state *st1, mach_state *st2) +{ +#if #cpu(sparc) + asm("ta 3"); // save register windows +#endif + if (! setjmp(st1->buf)) + longjmp(st2->buf, 1); +} + +static void +mach_start(mach_state *st1, void *pc, char *stacklo, char *stackhi) +{ +#if #cpu(sparc) + asm("ta 3"); // save register windows +#endif + if (! setjmp(st1->buf)) + { + // The following code must perform two tasks: + // -- set stack pointer to a proper value between #stacklo# and #stackhi#. + // -- branch to or call the function at address #pc#. + // This function never returns ... so there is no need to save anything +#if #cpu(mips) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("move $sp,%0\n\t" // set new stack pointer + "move $25,%1\n\t" // call subroutine via $25 + "jal $25\n\t" // call subroutine via $25 + "nop" // delay slot + : : "r" (sp), "r" (pc) ); +#elif #cpu(i386) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("movl %0,%%esp\n\t" // set new stack pointer + "call *%1" // call function + : : "r" (sp), "r" (pc) ); +#elif #cpu(sparc) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("ta 3\n\t" // saving the register windows will not hurt. + "mov %0,%%sp\n\t" // set new stack pointer + "call %1,0\n\t" // call function + "nop" // delay slot + : : "r" (sp), "r" (pc) ); +#elif #cpu(hppa) + char *sp = (char*)(((unsigned long)stacklo+128+255) & ~0xff); + asm volatile("copy %0,%%sp\n\t" // set stack pointer + "copy %1,%%r22\n\t" // set call address + ".CALL\n\t" // call pseudo instr (why?) + "bl $$dyncall,%%r31\n\t" // call + "copy %%r31,%%r2" // delay slot ??? + : : "r" (sp), "r" (pc) ); +#elif #cpu(alpha) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("bis $31,%0,$30\n\t" // set new stack pointer + "bis $31,%1,$27\n\t" // load function pointer + "jsr $26,($27),0" // call function + : : "r" (sp), "r" (pc) ); +#elif #cpu(powerpc) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("mr 1,%0\n\t" // set new stack pointer + "mr 0,%1\n\t" // load func pointer into r0 + "mtlr 0\n\t" // load link register with r0 + "blrl" // branch + : : "r" (sp), "r" (pc) ); +#elif #cpu(m68k) && defined(COTHREAD_UNTESTED) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("move%.l %0,%Rsp\n\t" // set new stack pointer + "jmp %a1" // branch to address %1 + : : "r" (sp), "a" (pc) ); +#elif #cpu(arm) && defined(COTHREAD_UNTESTED) + char *sp = (char*)(((unsigned long)stackhi-16) & ~0xff); + asm volatile ("mov%?\t%|sp, %0\n\t" // set new stack pointer + "mov%?\t%|pc, %1" // branch to address %1 + : : "r" (sp), "r" (pc) ); +#else +#error "COTHREADS not supported on this machine." +#error "Try -DTHREADMODEL=NOTHREADS." +#endif + // We should never reach this point + abort(); + // Note that this call to abort() makes sure + // that function mach_start() is compiled as a non-leaf + // function. It is indeed a non-leaf function since the + // piece of assembly code calls a function, but the compiler + // would not know without the call to abort() ... + } +} + +#ifdef CHECK +// This code can be used to verify that task switching works. +char stack[16384]; +mach_state st1, st2; +void th2() { + puts("2b"); mach_switch(&st2, &st1); + puts("4b"); mach_switch(&st2, &st1); + puts("6b"); mach_switch(&st2, &st1); +} +void th2relay() { + th2(); puts("ooops\n"); +} +void th1() { + mach_start(&st1, (void*)th2relay, stack, stack+sizeof(stack)); + puts("3a"); mach_switch(&st1, &st2); + puts("5a"); mach_switch(&st1, &st2); +} +int main() { + puts("1a"); th1(); puts("6a"); +} +#endif + + + +// -------------------------------------- select + +struct coselect { + int nfds; + fd_set rset; + fd_set wset; + fd_set eset; +}; + +static void +coselect_merge(coselect *dest, coselect *from) +{ + int i; + int nfds = from->nfds; + if (nfds > dest->nfds) + dest->nfds = nfds; + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->rset)) FD_SET(i, &dest->rset); + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->wset)) FD_SET(i, &dest->wset); + for (i=0; i<nfds; i++) if (FD_ISSET(i, &from->eset)) FD_SET(i, &dest->eset); +} + +static int +coselect_test(coselect *c) +{ + static timeval tmzero = {0,0}; + fd_set copyr = c->rset; + fd_set copyw = c->wset; + fd_set copye = c->eset; + return select(c->nfds, ©r, ©w, ©e, &tmzero); +} + + +// -------------------------------------- cotask + +class GThread::cotask { +public: +#ifndef NO_LIBGCC_HOOKS + cotask(const int xstacksize,void *); +#else + cotask(const int xstacksize); +#endif + ~cotask(); + class GThread::cotask *next; + class GThread::cotask *prev; + // context + mach_state regs; + // stack information + char *stack; + GPBuffer<char> gstack; + int stacksize; + // timing information + unsigned long over; + // waiting information + void *wchan; + coselect *wselect; + unsigned long *maxwait; + // delete after termination + bool autodelete; + // egcs exception support +#ifndef NO_LIBGCC_HOOKS + void *ehctx; +#endif +}; + +#ifndef NO_LIBGCC_HOOKS +GThread::cotask::cotask(const int xstacksize, void *xehctx) +#else +GThread::cotask::cotask(const int xstacksize) +#endif +: next(0), prev(0), gstack(stack,xstacksize), stacksize(xstacksize), + over(0), wchan(0), wselect(0), maxwait(0), autodelete(false) +#ifndef NO_LIBGCC_HOOKS + ,ehctx(xehctx) +#endif +{ + memset(®s,0,sizeof(regs)); +} + +static GThread::cotask *maintask = 0; +static GThread::cotask *curtask = 0; +static GThread::cotask *autodeletetask = 0; +static unsigned long globalmaxwait = 0; +static void (*scheduling_callback)(int) = 0; +static timeval time_base; + + +GThread::cotask::~cotask() +{ + gstack.resize(0); +#ifndef NO_LIBGCC_HOOKS + if (ehctx) + free(ehctx); + ehctx = 0; +#endif +} + +static void +cotask_free(GThread::cotask *task) +{ +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: freeing task %p with autodelete=%d\n", + task,task->autodelete); +#endif + if (task!=maintask) + { + delete task; + } +} + + +// -------------------------------------- time + +static unsigned long +time_elapsed(int reset=1) +{ + timeval tm; + gettimeofday(&tm, NULL); + long msec = (tm.tv_usec-time_base.tv_usec)/1000; + unsigned long elapsed = (long)(tm.tv_sec-time_base.tv_sec)*1000 + msec; + if (reset && elapsed>0) + { +#ifdef COTHREAD_TRACE +#ifdef COTHREAD_TRACE_VERBOSE + DjVuPrintErrorUTF8("cothreads: %4ld ms in task %p\n", elapsed, curtask); +#endif +#endif + time_base.tv_sec = tm.tv_sec; + time_base.tv_usec += msec*1000; + } + return elapsed; +} + + +// -------------------------------------- scheduler + +static int +cotask_yield() +{ + // ok + if (! maintask) + return 0; + // get elapsed time and return immediately when it is too small + unsigned long elapsed = time_elapsed(); + if (elapsed==0 && curtask->wchan==0 && curtask->prev && curtask->next) + return 0; + // adjust task running time + curtask->over += elapsed; + if (curtask->over > MAXPENALTY) + curtask->over = MAXPENALTY; + // start scheduling + reschedule: + // try unblocking tasks + GThread::cotask *n = curtask->next; + GThread::cotask *q = n; + do + { + if (q->wchan) + { + if (q->maxwait && *q->maxwait<=elapsed) + { + *q->maxwait = 0; + q->wchan=0; + q->maxwait=0; + q->wselect=0; + } + else if (q->wselect && globalmaxwait<=elapsed && coselect_test(q->wselect)) + { + q->wchan=0; + if (q->maxwait) + *q->maxwait -= elapsed; + q->maxwait = 0; + q->wselect=0; + } + if (q->maxwait) + *q->maxwait -= elapsed; + } + q = q->next; + } + while (q!=n); + // adjust globalmaxwait + if (globalmaxwait < elapsed) + globalmaxwait = MAXFDWAIT; + else + globalmaxwait -= elapsed; + // find best candidate + static int count; + unsigned long best = MAXPENALTY + 1; + GThread::cotask *r = 0; + count = 0; + q = n; + do + { + if (! q->wchan) + { + count += 1; + if (best > q->over) + { + r = q; + best = r->over; + } + } + q = q->next; + } + while (q != n); + // found + if (count > 0) + { + // adjust over + q = n; + do + { + q->over = (q->over>best ? q->over-best : 0); + q = q->next; + } + while (q != n); + // Switch + if (r != curtask) + { +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: ----- switch to %p [%ld]\n", r, best); +#endif + GThread::cotask *old = curtask; + curtask = r; + mach_switch(&old->regs, &curtask->regs); + } + // handle autodelete + if (autodeletetask && autodeletetask->autodelete) + cotask_free(autodeletetask); + autodeletetask = 0; + // return + if (count == 1) + return 1; + return 0; + } + // No task ready + count = 0; + unsigned long minwait = MAXWAIT; + coselect allfds; + allfds.nfds = 1; + FD_ZERO(&allfds.rset); + FD_ZERO(&allfds.wset); + FD_ZERO(&allfds.eset); + q = n; + do + { + if (q->maxwait || q->wselect) + count += 1; + if (q->maxwait && *q->maxwait<minwait) + minwait = *q->maxwait; + if (q->wselect) + coselect_merge(&allfds, q->wselect); + q = q->next; + } + while (q != n); + // abort on deadlock + if (count == 0) { + DjVuMessageLite::perror( ERR_MSG("GThreads.panic") ); + abort(); + } + // select + timeval tm; + tm.tv_sec = minwait/1000; + tm.tv_usec = 1000*(minwait-1000*tm.tv_sec); + select(allfds.nfds,&allfds.rset, &allfds.wset, &allfds.eset, &tm); + // reschedule + globalmaxwait = 0; + elapsed = time_elapsed(); + goto reschedule; +} + + +static void +cotask_terminate(GThread::cotask *task) +{ +#ifdef COTHREAD_TRACE + DjVuPrintErrorUTF8("cothreads: terminating task %p\n", task); +#endif + if (task && task!=maintask) + { + if (task->prev && task->next) + { + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackTerminate); + task->prev->next = task->next; + task->next->prev = task->prev; + // mark task as terminated + task->prev = 0; + // self termination + if (task == curtask) + { + if (task->autodelete) + autodeletetask = task; + cotask_yield(); + } + } + } +} + + +static void +cotask_wakeup(void *wchan, int onlyone) +{ + if (maintask && curtask) + { + GThread::cotask *n = curtask->next; + GThread::cotask *q = n; + do + { + if (q->wchan == wchan) + { + q->wchan=0; + q->maxwait=0; + q->wselect=0; + q->over = 0; + if (onlyone) + return; + } + q = q->next; + } + while (q!=n); + } +} + + +// -------------------------------------- select / get_select + +static int +cotask_select(int nfds, + fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *tm) +{ + // bypass + if (maintask==0 || (tm && tm->tv_sec==0 && tm->tv_usec<1000)) + return select(nfds, rfds, wfds, efds, tm); + // copy parameters + unsigned long maxwait = 0; + coselect parm; + // set waiting info + curtask->wchan = (void*)&parm; + if (rfds || wfds || efds) + { + parm.nfds = nfds; + if (rfds) { parm.rset=*rfds; } else { FD_ZERO(&parm.rset); } + if (wfds) { parm.wset=*wfds; } else { FD_ZERO(&parm.wset); } + if (efds) { parm.eset=*efds; } else { FD_ZERO(&parm.eset); } + curtask->wselect = &parm; + } + if (tm) + { + maxwait = time_elapsed(0) + tm->tv_sec*1000 + tm->tv_usec/1000; + curtask->maxwait = &maxwait; + } + // reschedule + cotask_yield(); + // call select to update masks + if (tm) + { + tm->tv_sec = maxwait/1000; + tm->tv_usec = 1000*(maxwait-1000*tm->tv_sec); + } + static timeval tmzero = {0,0}; + return select(nfds, rfds, wfds, efds, &tmzero); +} + + +static void +cotask_get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + unsigned long &timeout) +{ + int ready = 1; + unsigned long minwait = MAXWAIT; + unsigned long elapsed = time_elapsed(0); + coselect allfds; + allfds.nfds=0; + FD_ZERO(&allfds.rset); + FD_ZERO(&allfds.wset); + FD_ZERO(&allfds.eset); + if (curtask) + { + GThread::cotask *q=curtask->next; + while (q != curtask) + { + ready++; + if (q->wchan) + { + if (q->wselect) + coselect_merge(&allfds, q->wselect); + if (q->maxwait && *q->maxwait<minwait) + minwait = *q->maxwait; + ready--; + } + q = q->next; + } + } + timeout = 0; + nfds=allfds.nfds; + *rfds=allfds.rset; + *wfds=allfds.wset; + *efds=allfds.eset; + if (ready==1 && minwait>elapsed) + timeout = minwait-elapsed; +} + + + +// -------------------------------------- libgcc hook + +#ifndef NO_LIBGCC_HOOKS +// These are exported by Leon's patched version of libgcc.a +// Let's hope that the egcs people will include the patch in +// the distributions. +extern "C" +{ + extern void* (*__get_eh_context_ptr)(void); + extern void* __new_eh_context(void); +} + +// This function is called via the pointer __get_eh_context_ptr +// by the internal mechanisms of egcs. It must return the +// per-thread event handler context. This is necessary to +// implement thread safe exceptions on some machine and/or +// when flag -fsjlj-exception is set. +static void * +cotask_get_eh_context() +{ + if (curtask) + return curtask->ehctx; + else if (maintask) + return maintask->ehctx; + DjVuMessageLite::perror( ERR_MSG("GThreads.co_panic") ); + abort(); +} +#endif + + + +// -------------------------------------- GThread + +void +GThread::set_scheduling_callback(void (*call)(int)) +{ + if (scheduling_callback) + G_THROW( ERR_MSG("GThreads.dupl_callback") ); + scheduling_callback = call; +} + + +GThread::GThread(int stacksize) + : task(0), xentry(0), xarg(0) +{ + // check argument + if (stacksize < 0) + stacksize = DEFSTACK; + if (stacksize < MINSTACK) + stacksize = MINSTACK; + // initialization + if (! maintask) + { +#ifndef NO_LIBGCC_HOOKS + static GThread::cotask comaintask(0,(*__get_eh_context_ptr)()); + __get_eh_context_ptr = cotask_get_eh_context; +#else + static GThread::cotask comaintask(0); +#endif + maintask = &comaintask; +// memset(maintask, 0, sizeof(GThread::cotask)); + maintask->next = maintask; + maintask->prev = maintask; + gettimeofday(&time_base,NULL); + curtask = maintask; + } + // allocation +#ifndef NO_LIBGCC_HOOKS + task = new GThread::cotask(stacksize,__new_eh_context()); +#else + task = new GThread::cotask(stacksize); +#endif +} + + +GThread::~GThread() +{ + if (task && task!=maintask) + { + if (task->prev) // running + task->autodelete = true; + else + cotask_free(task); + task = 0; + } +} + +#if __GNUC__ >= 3 +# if __GNUC_MINOR__ >= 4 +# define noinline __attribute__((noinline,used)) +# elif __GNUC_MINOR >= 2 +# define noinline __attribute__((noinline)) +# endif +#endif +#ifndef noinline +# define noinline /**/ +#endif + +static noinline void startone(void); +static noinline void starttwo(GThread *thr); +static GThread * volatile starter; + +static void +startone(void) +{ + GThread *thr = starter; + mach_switch(&thr->task->regs, &curtask->regs); + // Registers may still contain an improper pointer + // to the exception context. We should neither + // register cleanups nor register handlers. + starttwo(thr); + abort(); +} + +static void +starttwo(GThread *thr) +{ + // Hopefully this function reacquires + // an exception context pointer. Therefore + // we can register the exception handlers. + // It is placed after ``startone'' to avoid inlining. +#ifdef __EXCEPTIONS + try + { +#endif + G_TRY + { + thr->xentry( thr->xarg ); + } + G_CATCH(ex) + { + ex.perror(); + DjVuMessageLite::perror( ERR_MSG("GThreads.uncaught") ); +#ifdef _DEBUG + abort(); +#endif + } + G_ENDCATCH; +#ifdef __EXCEPTIONS + } + catch(...) + { + DjVuMessageLite::perror( ERR_MSG("GThreads.unrecognized") ); +#ifdef _DEBUG + abort(); +#endif + } +#endif + cotask_terminate(curtask); + GThread::yield(); + // Do not add anything below this line! + // Nothing should reach it anyway. + abort(); +} + +int +GThread::create(void (*entry)(void*), void *arg) +{ + if (task->next || task->prev) + return -1; + xentry = entry; + xarg = arg; + task->wchan = 0; + task->next = curtask; + task->prev = curtask->prev; + task->next->prev = task; + task->prev->next = task; + GThread::cotask *old = curtask; + starter = this; + mach_start(&old->regs, (void*)startone, + task->stack, task->stack+task->stacksize); + if (scheduling_callback) + (*scheduling_callback)(CallbackCreate); + return 0; +} + + +void +GThread::terminate() +{ + if (task && task!=maintask) + cotask_terminate(task); +} + +int +GThread::yield() +{ + return cotask_yield(); +} + +int +GThread::select(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + return cotask_select(nfds, readfds, writefds, exceptfds, timeout); +} + +void +GThread::get_select(int &nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + unsigned long &timeout) +{ + cotask_get_select(nfds, rfds, wfds, efds, timeout); +} + +inline void * +GThread::current() +{ + if (curtask && curtask!=maintask) + return (void*)curtask; + return (void*)0; +} + + +// -------------------------------------- GMonitor + +GMonitor::GMonitor() + : count(1), locker(0), wlock(0), wsig(0) +{ + locker = 0; + ok = 1; +} + +GMonitor::~GMonitor() +{ + ok = 0; + cotask_wakeup((void*)&wsig, 0); + cotask_wakeup((void*)&wlock, 0); + cotask_yield(); + // Because we know how the scheduler works, we know that this single call to + // yield will run all unblocked tasks and given them the chance to leave the + // scope of the monitor object. +} + +void +GMonitor::enter() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + { + while (ok && count<=0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = 1; + locker = self; + } + count -= 1; +} + +void +GMonitor::leave() +{ + void *self = GThread::current(); + if (ok && (count>0 || self!=locker)) + G_THROW( ERR_MSG("GThreads.not_acq_leave") ); + if (++count > 0 && wlock > 0) + cotask_wakeup((void*)&wlock, 1); +} + +void +GMonitor::signal() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_signal") ); + if (wsig > 0) + { + cotask_wakeup((void*)&wsig, 1); + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackUnblock); + } +} + +void +GMonitor::broadcast() +{ + void *self = GThread::current(); + if (count>0 || self!=locker) + G_THROW( ERR_MSG("GThreads.not_acq_broad") ); + if (wsig > 0) + { + cotask_wakeup((void*)&wsig, 0); + if (scheduling_callback) + (*scheduling_callback)(GThread::CallbackUnblock); + } +} + +void +GMonitor::wait() +{ + // Check state + void *self = GThread::current(); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + int sav_count = count; + count = 1; + curtask->wchan = (void*)&wsig; + cotask_wakeup((void*)&wlock, 1); + wsig++; + cotask_yield(); + wsig--; + // Re-acquire + while (ok && count <= 0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = sav_count; + locker = self; + } +} + +void +GMonitor::wait(unsigned long timeout) +{ + // Check state + void *self = GThread::current(); + if (count>0 || locker!=self) + G_THROW( ERR_MSG("GThreads.not_acq_wait") ); + // Wait + if (ok) + { + // Atomically release monitor and wait + int sav_count = count; + count = 1; + unsigned long maxwait = time_elapsed(0) + timeout; + curtask->maxwait = &maxwait; + curtask->wchan = (void*)&wsig; + cotask_wakeup((void*)&wlock, 1); + wsig++; + cotask_yield(); + wsig--; + // Re-acquire + while (ok && count<=0) + { + curtask->wchan = (void*)&wlock; + wlock++; + cotask_yield(); + wlock--; + } + count = sav_count; + locker = self; + } +} + +#endif + + + + +// ---------------------------------------- +// GSAFEFLAGS +// ---------------------------------------- + + + +GSafeFlags & +GSafeFlags::operator=(long xflags) +{ + enter(); + if (flags!=xflags) + { + flags=xflags; + broadcast(); + } + leave(); + return *this; +} + +GSafeFlags::operator long(void) const +{ + long f; + ((GSafeFlags *) this)->enter(); + f=flags; + ((GSafeFlags *) this)->leave(); + return f; +} + +bool +GSafeFlags::test_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1) +{ + enter(); + if ((flags & set_mask)==set_mask && + (~flags & clr_mask)==clr_mask) + { + long new_flags=flags; + new_flags|=set_mask1; + new_flags&=~clr_mask1; + if (new_flags!=flags) + { + flags=new_flags; + broadcast(); + } + leave(); + return true; + } + leave(); + return false; +} + +void +GSafeFlags::wait_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1) +{ + enter(); + while((flags & set_mask)!=set_mask || + (~flags & clr_mask)!=clr_mask) wait(); + long new_flags=flags; + new_flags|=set_mask1; + new_flags&=~clr_mask1; + if (flags!=new_flags) + { + flags=new_flags; + broadcast(); + } + leave(); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GThreads.h b/kviewshell/plugins/djvu/libdjvu/GThreads.h new file mode 100644 index 00000000..92691db4 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GThreads.h @@ -0,0 +1,639 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GTHREADS_H_ +#define _GTHREADS_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name GThreads.h + + Files #"GThreads.h"# and #"GThreads.cpp"# implement common entry points + for multithreading on multiple platforms. Each execution thread is + represented by an instance of class \Ref{GThread}. Synchronization is + provided by class \Ref{GMonitor} which implements a monitor (C.A.R Hoare, + Communications of the ACM, 17(10), 1974). + + The value of compiler symbol #THREADMODEL# selects an appropriate + implementation for these classes. The current implementation supports + the following values: + \begin{description} + \item[-DTHREADMODEL=NOTHREADS] Dummy implementation. This is a + good choice when the multithreading features are not required, + because it minimizes the portability problems. This is currently + the default when compiling under Unix. + \item[-DTHREADMODEL=WINTHREADS] Windows implementation. + This is the default when compiling under Windows. + \item[-DTHREADMODEL=MACTHREADS] Macintosh implementation, + which is based on the MacOS cooperative model. The current + implementation does not yet fully support synchronization. + This is the default when compiling under MacOS. + \item[-DTHREADMODEL=POSIXTHREADS] Posix implementation. + This implementation also supports DCE threads. The behavior of + the code is subject to the quality of the system implementation of + Posix threads. + \item[-DTHREADMODEL=COTHREADS] Custom cooperative threads. + These custom threads do not redefine system calls. Before executing + a potentially blocking system function, each thread must explicitly + check whether it is going to block and yield control explicitly if + this is the case. This code must be compiled with a patched version + of egcs-1.1.1 \URL{http://egcs.cygnus.com}. The patch addresses + exception thread-safety and is provided in #"@Tools/libgcc2.c.diff"#. + Once you get the right compiler, this implementation is remarkably + compact and portable. A variety of processors are supported, + including mips, intel, sparc, hppa, and alpha. + \item[-DTHREADMODEL=JRITHREADS] Java implementation hooks. + Multi-threading within a Netscape plugin can be tricky. A simple + idea however consists of implementing the threading primitives in + Java and to access them using JRI. The classes just contain a + JRIGlobalRef. This is not a real implementation since everything + (Java code, native functions, stubs, exception thread safety) must + be addressed by the plugin source code. Performance may be a serious + issue. + \end{description} + + {\bf Portability}: The simultaneous use of threads and exceptions caused a + lot of portability headaches under Unix. We eventually decided to + implement the COTHREADS cooperative threads (because preemptive threads + have more problems) and to patch EGCS in order to make exception handling + COTHREAD-safe. + + @memo + Portable threads + @author + L\'eon Bottou <[email protected]> -- initial implementation.\\ + Praveen Guduru <[email protected]> -- mac implementation. + +// From: Leon Bottou, 1/31/2002 +// Almost unchanged by Lizardtech. +// GSafeFlags should go because it not as safe as it claims. + + @version + #$Id: GThreads.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include "GException.h" + +#define NOTHREADS 0 +#define COTHREADS 1 +#define JRITHREADS 2 +#define POSIXTHREADS 10 +#define WINTHREADS 11 +#define MACTHREADS 12 + +// Known platforms +#ifndef THREADMODEL +#if defined(WIN32) +#define THREADMODEL WINTHREADS +#endif +#if defined(macintosh) +#define THREADMODEL MACTHREADS +#endif +#endif + +// Exception emulation is not thread safe +#ifdef USE_EXCEPTION_EMULATION +#undef THREADMODEL +#define THREADMODEL NOTHREADS +#endif +// Default is nothreads +#ifndef THREADMODEL +#define THREADMODEL NOTHREADS +#endif + +// ---------------------------------------- +// INCLUDES + +#if THREADMODEL==WINTHREADS +#ifndef _WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif +#endif + +#if THREADMODEL==MACTHREADS +#include <threads.h> +#endif + +#if THREADMODEL==POSIXTHREADS +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#undef TRY +#undef CATCH +#define _CMA_NOWRAPPERS_ +#include <pthread.h> +#endif + +#if THREADMODEL==JRITHREADS +#include "jri.h" +#endif + +#if THREADMODEL==COTHREADS +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#endif + + +// ---------------------------------------- +// PORTABLE CLASSES + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** Thread class. A multithreaded process is composed of a main execution + thread and of several secondary threads. Each secondary thread is + represented by a #GThread# object. The amount of memory required for the + stack of a secondary thread is defined when the #GThread# object is + constructed. The execution thread is started when function + \Ref{GThread::create} is called. The destructor of class GThread waits + until the thread terminanes. Note that the execution can be terminated at + any time (with possible prejudice) by calling \Ref{GThread::terminate}. + + Several static member functions control the thread scheduler. Function + \Ref{GThread::yield} relinquishes the processor to another thread. + Function \Ref{GThread::select} (#COTHREADS# only) provides a thread-aware + replacement for the well-known unix system call #select#. + + {\bf Note} --- Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. */ + +class GThread { +public: + /** Constructs a new thread object. Memory is allocated for the + thread, but the thread is not started. + Argument #stacksize# is used by the #COTHREADS# model only for + specifying the amount of memory needed for the processor stack. A + negative value will be replaced by a suitable default value of 128Kb. + A minimum value of 32Kb is silently enforced. */ + GThread(int stacksize = -1); + /** Destructor. Destroying the thread object while the thread is running is + perfectly ok since it only destroys the thread identifier. Execution + will continue without interference. */ + ~GThread(); + /** Starts the thread. The new thread executes function #entry# with + argument #arg#. The thread terminates when the function returns. A + thread cannot be restarted after its termination. You must create a new + #GThread# object. */ + int create(void (*entry)(void*), void *arg); + /** Terminates a thread with extreme prejudice. The thread is removed from + the scheduling list. Execution terminates regardless of the execution + status of the thread function. Automatic variables may or may not be + destroyed. This function must be considered as a last resort since + memory may be lost. */ + void terminate(); + /** Causes the current thread to relinquish the processor. The scheduler + selects a thread ready to run and transfers control to that thread. The + actual effect of #yield# heavily depends on the selected implementation. + Function #yield# usually returns zero when the execution of the current + thread is resumed. It may return a positive number when it can + determine that the current thread will remain the only runnable thread + for some time. You may then call function \Ref{get_select} to + obtain more information. */ + static int yield(); + /** Returns a value which uniquely identifies the current thread. */ + static void *current(); + +#if THREADMODEL==WINTHREADS +private: + HANDLE hthr; + DWORD thrid; +#elif THREADMODEL==MACTHREADS +private: + unsigned long thid; + static pascal void *start(void *arg); +#elif THREADMODEL==POSIXTHREADS +private: + pthread_t hthr; + static void *start(void *arg); +#elif THREADMODEL==JRITHREADS +private: + JRIGlobalRef obj; +#elif THREADMODEL==COTHREADS + friend class GMonitor; +public: + class cotask; + class cotask *task; + /** Replaces system call #select# (COTHREADS only). The #COTHREADS# model + does not redefine system function. System functions therefore can + potentially block the whole process (instead of blocking the current + thread only) because the system is not aware of the #COTHREADS# + scheduler. The function #GThread::select# is a #COTHREADS#-aware + replacement for the well known system function #select#. You can also + use #GThread::select# for making sure that calls to system functions + will not block the entire process, as demonstrated below: + \begin{verbatim} + int + gthread_read(int fd, void *buffer, size_t len) + { + fd_set rdset; + FD_ZERO(&rdset); + FD_SET(fd, &rdset); + GThread::select(fd+1, &rdset, 0, 0, 0); + return read(fd, buffer, len); + } + \end{verbatim} */ + static int select(int nfds, fd_set*, fd_set*, fd_set*, struct timeval*); + /** Provide arguments for system call #select# (COTHREADS only). It may be + appropriate to call the real system call #select# if the current thread + is the only thread ready to run. Other threads however may wake up when + certain file descriptors are ready or when a certain delay expires. + Function #get_select# returns this information by filling the three + usual file descriptor sets (similar to the arguments of system call + #select#). It also returns a timeout #timeout# expressed in + milliseconds. Note that this timeout is zero is the current thread is + not the sole thread ready to run. */ + static void get_select(int &nfds, fd_set*, fd_set*, fd_set*, unsigned long &timeout); + /** Install hooks in the scheduler (COTHREADS only). The hook function + #call# is called when a new thread is created (argument is + #GThread::CallbackCreate#), when a thread terminates (argument is + #GThread::CallbackTerminate#), or when thread is unblocked (argument is + #GThread::CallbackUnblock#). This callback can be useful in certain GUI + toolkits where the most convenient method for scheduling the threads + consists in setting a timer event that calls \Ref{GThread::yield}. */ + static void set_scheduling_callback(void (*call)(int)); + enum { CallbackCreate, CallbackTerminate, CallbackUnblock }; + +#endif +public: + // Should be considered as private + void (*xentry)(void*); + void *xarg; +private: + // Disable default members + GThread(const GThread&); + GThread& operator=(const GThread&); +}; + + +/** Monitor class. Monitors have been first described in (C.A.R Hoare, + Communications of the ACM, 17(10), 1974). This mechanism provides the + basic mutual exclusion (mutex) and thread notification facilities + (condition variables). + + Only one thread can own the monitor at a given time. Functions + \Ref{enter} and \Ref{leave} can be used to acquire and release the + monitor. This mutual exclusion provides an efficient way to protect + segment of codes ({\em critical sections}) which should not be + simultaneously executed by two threads. Class \Ref{GMonitorLock} provides + a convenient way to do this effectively. + + When the thread owning the monitor calls function \Ref{wait}, the monitor + is released and the thread starts waiting until another thread calls + function \Ref{signal} or \Ref{broadcast}. When the thread wakes-up, it + re-acquires the monitor and function #wait# returns. Since the signaling + thread must acquire the monitor before calling functions #signal# and + #broadcast#, the signaled thread will not be able to re-acquire the + monitor until the signaling thread(s) releases the monitor. + + {\bf Note} --- Both the copy constructor and the copy operator are declared + as private members. It is therefore not possible to make multiple copies + of instances of this class, as implied by the class semantic. */ + +class GMonitor +{ +public: + GMonitor(); + ~GMonitor(); + /** Enters the monitor. If the monitor is acquired by another thread this + function waits until the monitor is released. The current thread then + acquires the monitor. Calls to #enter# and #leave# may be nested. */ + void enter(); + /** Leaves the monitor. The monitor counts how many times the current + thread has entered the monitor. Function #leave# decrement this count. + The monitor is released when this count reaches zero. An exception is + thrown if this function is called by a thread which does not own the + monitor. */ + void leave(); + /** Waits until the monitor is signaled. The current thread atomically + releases the monitor and waits until another thread calls function + #signal# or #broadcast#. Function #wait# then re-acquires the monitor + and returns. An exception is thrown if this function is called by a + thread which does not own the monitor. */ + void wait(); + /** Waits until the monitor is signaled or a timeout is reached. The + current thread atomically releases the monitor and waits until another + thread calls function #signal# or #broadcast# or a maximum of #timeout# + milliseconds. Function #wait# then re-acquires the monitor and returns. + An exception is thrown if this function is called by a thread which does + not own the monitor. */ + void wait(unsigned long timeout); + /** Signals one waiting thread. Function #signal# wakes up at most one of + the waiting threads for this monitor. An exception is thrown if this + function is called by a thread which does not own the monitor. */ + void signal(); + /** Signals all waiting threads. Function #broadcast# wakes up all the + waiting threads for this monitor. An exception is thrown if this + function is called by a thread which does not own the monitor. */ + void broadcast(); +private: +#if THREADMODEL==WINTHREADS + int ok; + int count; + DWORD locker; + CRITICAL_SECTION cs; + struct thr_waiting *head; + struct thr_waiting *tail; +#elif THREADMODEL==MACTHREADS + int ok; + int count; + unsigned long locker; + int wlock; + int wsig; +#elif THREADMODEL==POSIXTHREADS + int ok; + int count; + pthread_t locker; + pthread_mutex_t mutex; + pthread_cond_t cond; +#elif THREADMODEL==COTHREADS + int ok; + int count; + void *locker; + int wlock; + int wsig; +#elif THREADMODEL==JRITHREADS + JRIGlobalRef obj; +#endif +private: + // Disable default members + GMonitor(const GMonitor&); + GMonitor& operator=(const GMonitor&); +}; + + + + +// ---------------------------------------- +// NOTHREADS INLINES + +#if THREADMODEL==NOTHREADS +inline GThread::GThread(int) {} +inline GThread::~GThread(void) {} +inline void GThread::terminate() {} +inline int GThread::yield() { return 0; } +inline void* GThread::current() { return 0; } +inline GMonitor::GMonitor() {} +inline GMonitor::~GMonitor() {} +inline void GMonitor::enter() {} +inline void GMonitor::leave() {} +inline void GMonitor::wait() {} +inline void GMonitor::wait(unsigned long) {} +inline void GMonitor::signal() {} +inline void GMonitor::broadcast() {} +#endif // NOTHREADS + + +// ---------------------------------------- +// SCOPE LOCK + + +/** Wrapper for mutually exclusive code. + This class locks a specified critical section (see \Ref{GCriticalSection}) + at construction time and unlocks it at destruction time. It provides a + convenient way to take advantage of the C++ implicit destruction of + automatic variables in order to make sure that the monitor is + released when exiting the protected code. The following code will release + the monitor when the execution thread leaves the protected scope, either + because the protected code has executed successfully, or because an + exception was thrown. + \begin{verbatim} + { -- protected scope + static GMonitor theMonitor; + GMonitorLock lock(&theMonitor) + ... -- protected code + } + \end{verbatim} + This construct will do nothing when passed a null pointer. +*/ +class GMonitorLock +{ +private: + GMonitor *gsec; +public: + /** Constructor. Enters the monitor #gsec#. */ + GMonitorLock(GMonitor *gsec) : gsec(gsec) + { if (gsec) gsec->enter(); }; + /** Destructor. Leaves the associated monitor. */ + ~GMonitorLock() + { if (gsec) gsec->leave(); }; +}; + + + +// ---------------------------------------- +// GSAFEFLAGS (not so safe) + + +/** A thread safe class representing a set of flags. The flags are protected + by \Ref{GMonitor}, which is attempted to be locked whenever somebody + accesses the flags. One can modify the class contents using one of + two functions: \Ref{test_and_modify}() and \Ref{wait_and_modify}(). + Both of them provide atomic operation of testing (first) and modification + (second). The flags remain locked between the moment of testing and + modification, which guarantees, that their state cannot be changed in + between of these operations. */ +class GSafeFlags : public GMonitor +{ +private: + volatile long flags; +public: + /// Constructs #GSafeFlags# object. + GSafeFlags(long flags=0); + + /** Assignment operator. Will also wake up threads waiting for the + flags to change. */ + GSafeFlags & operator=(long flags); + + /** Returns the value of the flags */ + operator long(void) const; + /** Modifies the flags by ORing them with the provided mask. A broadcast + will be sent after the modification is done. */ + GSafeFlags & operator|=(long mask); + /** Modifies the flags by ANDing them with the provided mask. A broadcast + will be sent after the modification is done. */ + GSafeFlags & operator&=(long mask); + + /** If all bits mentioned in #set_mask# are set in the flags and all + bits mentioned in #clr_mask# are cleared in the flags, it sets all + bits from #set_mask1# in the flags, clears all flags from + #clr_mask1# in the flags and returns #TRUE#. Otherwise returns + #FALSE#. */ + bool test_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1); + + /** Waits until all bits mentioned in #set_mask# are set in the flags + and all bits mentioned in #clr_flags# are cleared in the flags. + After that it sets bits from #set_mask1# and clears bits from + #clr_mask1# in the flags. */ + void wait_and_modify(long set_mask, long clr_mask, + long set_mask1, long clr_mask1); + + /** Waits until all bits set in #set_mask# are set in the flags and + all bits mentioned in #clr_mask# are cleared in the flags. */ + void wait_for_flags(long set_mask, long clr_mask=0) const; + + /** Modifies the flags by setting all bits mentioned in #set_mask# + and clearing all bits mentioned in #clr_mask#. If the flags have + actually been modified, a broadcast will be sent. */ + void modify(long set_mask, long clr_mask); +}; + +inline +GSafeFlags::GSafeFlags(long xflags) + : flags(xflags) +{ +} + +inline void +GSafeFlags::wait_for_flags(long set_mask, long clr_mask) const +{ + ((GSafeFlags *) this)->wait_and_modify(set_mask, clr_mask, 0, 0); +} + +inline void +GSafeFlags::modify(long set_mask, long clr_mask) +{ + test_and_modify(0, 0, set_mask, clr_mask); +} + +inline GSafeFlags & +GSafeFlags::operator|=(long mask) +{ + test_and_modify(0, 0, mask, 0); + return *this; +} + +inline GSafeFlags & +GSafeFlags::operator&=(long mask) +{ + test_and_modify(0, 0, 0, ~mask); + return *this; +} + +//@} + + + + +// ---------------------------------------- +// COMPATIBILITY CLASSES + + +// -- these classes are no longer documented. + +class GCriticalSection : protected GMonitor +{ +public: + void lock() + { GMonitor::enter(); }; + void unlock() + { GMonitor::leave(); }; +}; + +class GEvent : protected GMonitor +{ +private: + int status; +public: + GEvent() + : status(0) { }; + void set() + { if (!status) { enter(); status=1; signal(); leave(); } }; + void wait() + { enter(); if (!status) GMonitor::wait(); status=0; leave(); }; + void wait(int timeout) + { enter(); if (!status) GMonitor::wait(timeout); status=0; leave(); }; +}; + +class GCriticalSectionLock +{ +private: + GCriticalSection *gsec; +public: + GCriticalSectionLock(GCriticalSection *gsec) : gsec(gsec) + { if (gsec) gsec->lock(); }; + ~GCriticalSectionLock() + { if (gsec) gsec->unlock(); }; +}; + + +// ---------------------------------------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif //_GTHREADS_H_ + diff --git a/kviewshell/plugins/djvu/libdjvu/GURL.cpp b/kviewshell/plugins/djvu/libdjvu/GURL.cpp new file mode 100644 index 00000000..54b082ff --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GURL.cpp @@ -0,0 +1,1965 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GURL.cpp,v 1.19 2005/04/27 16:34:13 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This has been heavily changed by Lizardtech. +// They decided to use URLs for everyting, including +// the most basic file access. The URL class now is a unholy +// mixture of code for syntactically parsing the urls (which it was) +// and file status code (only for local file: urls). + +#include "GException.h" +#include "GOS.h" +#include "GURL.h" +#include "debug.h" + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +#ifdef WIN32 +# include <atlbase.h> +# include <windows.h> +# include <direct.h> +#endif /* WIN32 */ + +// -- MAXPATHLEN +#ifndef MAXPATHLEN +# ifdef _MAX_PATH +# define MAXPATHLEN _MAX_PATH +# else +# define MAXPATHLEN 1024 +# endif +#else +# if ( MAXPATHLEN < 1024 ) +# undef MAXPATHLEN +# define MAXPATHLEN 1024 +# endif +#endif + +#if defined(UNIX) || defined(OS2) +# include <unistd.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <errno.h> +# include <fcntl.h> +# include <pwd.h> +# include <stdio.h> +# ifdef AUTOCONF +# ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +# else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +# endif +# ifdef HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +# endif +# else /* !AUTOCONF */ +# include <sys/time.h> +# if defined(XENIX) +# define USE_DIRECT +# include <sys/ndir.h> +# elif defined(OLDBSD) +# define USE_DIRECT +# include <sys/dir.h> +# endif +# ifdef USE_DIRECT +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# else +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +# endif +# endif /* !AUTOCONF */ +#endif /* UNIX */ + +#ifdef macintosh +#include <unix.h> +#include <errno.h> +#include <unistd.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const char djvuopts[]="DJVUOPTS"; +static const char localhost[]="file://localhost/"; +static const char backslash='\\'; +static const char colon=':'; +static const char dot='.'; +static const char filespecslashes[] = "file://"; +static const char filespec[] = "file:"; +static const char slash='/'; +static const char percent='%'; +static const char localhostspec1[] = "//localhost/"; +static const char localhostspec2[] = "///"; +static const char nillchar=0; +#if defined(UNIX) + static const char tilde='~'; + static const char root[] = "/"; +#elif defined(WIN32) || defined(OS2) + static const char root[] = "\\"; +#elif defined(macintosh) + static char const * const root = &nillchar; +#else +#error "Define something here for your operating system" +#endif + + +static const int +pathname_start(const GUTF8String &url, const int protolength); + +// hexval -- +// -- Returns the hexvalue of a character. +// Returns -1 if illegal; + +static int +hexval(char c) +{ + return ((c>='0' && c<='9') + ?(c-'0') + :((c>='A' && c<='F') + ?(c-'A'+10) + :((c>='a' && c<='f') + ?(c-'a'+10):(-1)))); +} + + +static bool +is_argument(const char * start) + // Returns TRUE if 'start' points to the beginning of an argument + // (either hash or CGI) +{ + // return (*start=='#' || *start=='?' || *start=='&' || *start==';'); + return (*start=='#' || *start=='?' ); +} + +static bool +is_argument_sep(const char * start) + // Returns TRUE if 'start' points to the beginning of an argument + // (either hash or CGI) +{ + return (*start=='&')||(*start == ';'); +} + +void +GURL::convert_slashes(void) +{ + GUTF8String xurl(get_string()); +#if defined(WIN32) + const int protocol_length=protocol(xurl).length(); + for(char *ptr=(xurl.getbuf()+protocol_length);*ptr;ptr++) + if(*ptr == backslash) + *ptr=slash; + url=xurl; +#endif +} + +static void +collapse(char * ptr, const int chars) + // Will remove the first 'chars' chars from the string and + // move the rest toward the beginning. Will take into account + // string length +{ + const int length=strlen(ptr); + const char *srcptr=ptr+((chars>length)?length:chars); + while((*(ptr++) = *(srcptr++))) + EMPTY_LOOP; +} + +GUTF8String +GURL::beautify_path(GUTF8String xurl) +{ + + const int protocol_length=GURL::protocol(xurl).length(); + + // Eats parts like ./ or ../ or /// + char * buffer; + GPBuffer<char> gbuffer(buffer,xurl.length()+1); + strcpy(buffer, (const char *)xurl); + + // Find start point + char * start=buffer+pathname_start(xurl,protocol_length); + + // Find end of the url (don't touch arguments) + char * ptr; + GUTF8String args; + for(ptr=start;*ptr;ptr++) + { + if (is_argument(ptr)) + { + args=ptr; + *ptr=0; + break; + } + } + + // Eat multiple slashes + for(;(ptr=strstr(start, "////"));collapse(ptr, 3)) + EMPTY_LOOP; + for(;(ptr=strstr(start, "//"));collapse(ptr, 1)) + EMPTY_LOOP; + // Convert /./ stuff into plain / + for(;(ptr=strstr(start, "/./"));collapse(ptr, 2)) + EMPTY_LOOP; +#if defined(WIN32) || defined(OS2) + if(!xurl.cmp(filespec,sizeof(filespec)-1)) + { + int offset=1; + if(start&&(start[0] == '/')&& + !xurl.cmp("file:////",sizeof("file:////")-1)) + { + collapse(start, 1); + offset=0; + } + for(ptr=start+offset;(ptr=strchr(ptr, '/'));) + { + if(isalpha((++ptr)[0])) + { + if((ptr[1] == ':')&&(ptr[2]=='/')) + { + char *buffer2; + GPBuffer<char> gbuffer2(buffer2,strlen(ptr)+1); + strcpy(buffer2,ptr); + gbuffer.resize(strlen(ptr)+sizeof(localhost)); + strcpy(buffer,localhost); + strcat(buffer,buffer2); + ptr=(start=buffer+sizeof(localhost))+1; + } + } + } + } +#endif + // Process /../ + while((ptr=strstr(start, "/../"))) + { + for(char * ptr1=ptr-1;(ptr1>=start);ptr1--) + { + if (*ptr1==slash) + { + collapse(ptr1, ptr-ptr1+3); + break; + } + } + } + + // Remove trailing /. + ptr=start+strlen(start)-2; + if((ptr>=start)&& (ptr == GUTF8String("/."))) + { + ptr[1]=0; + } + // Eat trailing /.. + ptr=start+strlen(start)-3; + if((ptr >= start) && (ptr == GUTF8String("/.."))) + { + for(char * ptr1=ptr-1;(ptr1>=start);ptr1--) + { + if (*ptr1==slash) + { + ptr1[1]=0; + break; + } + } + } + + // Done. Copy the buffer back into the URL and add arguments. + xurl=buffer; + return (xurl+args); +} + + +void +GURL::beautify_path(void) +{ + url=beautify_path(get_string()); +} + +void +GURL::init(const bool nothrow) +{ + GCriticalSectionLock lock(&class_lock); + validurl=true; + + if (url.length()) + { + GUTF8String proto=protocol(); + if (proto.length()<2) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.no_protocol") "\t"+url); + return; + } + + // Below we have to make this complex test to detect URLs really + // referring to *local* files. Surprisingly, file://hostname/dir/file + // is also valid, but shouldn't be treated thru local FS. + if (proto=="file" && url[5]==slash && + (url[6]!=slash || !url.cmp(localhost, sizeof(localhost)))) + { + // Separate the arguments + GUTF8String arg; + { + const char * const url_ptr=url; + const char * ptr; + for(ptr=url_ptr;*ptr&&!is_argument(ptr);ptr++) + EMPTY_LOOP; + arg=ptr; + url=url.substr(0,(size_t)ptr-(size_t)url_ptr); + } + + // Do double conversion + GUTF8String tmp=UTF8Filename(); + if (!tmp.length()) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.fail_to_file") ); + return; + } + url=GURL::Filename::UTF8(tmp).get_string(); + if (!url.length()) + { + validurl=false; + if(!nothrow) + G_THROW( ERR_MSG("GURL.fail_to_URL") ); + return; + } + // Return the argument back + url+=arg; + } + convert_slashes(); + beautify_path(); + parse_cgi_args(); + } +} + +GURL::GURL(void) + : validurl(false) +{ +} + +GURL::GURL(const char * url_in) + : url(url_in ? url_in : ""), validurl(false) +{ +} + +GURL::GURL(const GUTF8String & url_in) + : url(url_in), validurl(false) +{ +} + +GURL::GURL(const GNativeString & url_in) + : url(url_in.getNative2UTF8()), validurl(false) +{ +#if defined(WIN32) || defined(OS2) + if(is_valid() && is_local_file_url()) + { + GURL::Filename::UTF8 xurl(UTF8Filename()); + url=xurl.get_string(true); + validurl=false; + } +#endif +} + +GURL::GURL(const GURL & url_in) + : validurl(false) +{ + if(url_in.is_valid()) + { + url=url_in.get_string(); + init(); + }else + { + url=url_in.url; + } +} + +GURL & +GURL::operator=(const GURL & url_in) +{ + GCriticalSectionLock lock(&class_lock); + if(url_in.is_valid()) + { + url=url_in.get_string(); + init(true); + }else + { + url=url_in.url; + validurl=false; + } + return *this; +} + +GUTF8String +GURL::protocol(const GUTF8String& url) +{ + const char * const url_ptr=url; + const char * ptr=url_ptr; + for(char c=*ptr; + c && (isalnum(c) || c == '+' || c == '-' || c == '.'); + c=*(++ptr)) EMPTY_LOOP; + return(*ptr==colon)?GUTF8String(url_ptr, ptr-url_ptr):GUTF8String(); +} + +GUTF8String +GURL::hash_argument(void) const + // Returns the HASH argument (anything after '#' and before '?') +{ + const GUTF8String xurl(get_string()); + + bool found=false; + GUTF8String arg; + + // Break if CGI argument is found + for(const char * start=xurl;*start&&(*start!='?');start++) + { + if (found) + { + arg+=*start; + }else + { + found=(*start=='#'); + } + } + return decode_reserved(arg); +} + +void +GURL::set_hash_argument(const GUTF8String &arg) +{ + const GUTF8String xurl(get_string()); + + GUTF8String new_url; + bool found=false; + const char * ptr; + for(ptr=xurl;*ptr;ptr++) + { + if (is_argument(ptr)) + { + if (*ptr!='#') + { + break; + } + found=true; + } else if (!found) + { + new_url+=*ptr; + } + } + + url=new_url+"#"+GURL::encode_reserved(arg)+ptr; +} + +void +GURL::parse_cgi_args(void) + // Will read CGI arguments from the URL into + // cgi_name_arr and cgi_value_arr +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + cgi_name_arr.empty(); + cgi_value_arr.empty(); + + // Search for the beginning of CGI arguments + const char * start=url; + while(*start) + { + if(*(start++)=='?') + { + break; + } + } + + // Now loop until we see all of them + while(*start) + { + GUTF8String arg; // Storage for another argument + while(*start) // Seek for the end of it + { + if (is_argument_sep(start)) + { + start++; + break; + } else + { + arg+=*start++; + } + } + if (arg.length()) + { + // Got argument in 'arg'. Split it into 'name' and 'value' + const char * ptr; + const char * const arg_ptr=arg; + for(ptr=arg_ptr;*ptr&&(*ptr != '=');ptr++) + EMPTY_LOOP; + + GUTF8String name, value; + if (*ptr) + { + name=GUTF8String(arg_ptr, (int)((ptr++)-arg_ptr)); + value=GUTF8String(ptr, arg.length()-name.length()-1); + } else + { + name=arg; + } + + int args=cgi_name_arr.size(); + cgi_name_arr.resize(args); + cgi_value_arr.resize(args); + cgi_name_arr[args]=decode_reserved(name); + cgi_value_arr[args]=decode_reserved(value); + } + } +} + +void +GURL::store_cgi_args(void) + // Will store CGI arguments from the cgi_name_arr and cgi_value_arr + // back into the URL +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + const char * const url_ptr=url; + const char * ptr; + for(ptr=url_ptr;*ptr&&(*ptr!='?');ptr++) + EMPTY_LOOP; + + GUTF8String new_url(url_ptr, ptr-url_ptr); + + for(int i=0;i<cgi_name_arr.size();i++) + { + GUTF8String name=GURL::encode_reserved(cgi_name_arr[i]); + GUTF8String value=GURL::encode_reserved(cgi_value_arr[i]); + new_url+=(i?"&":"?")+name; + if (value.length()) + new_url+="="+value; + } + + url=new_url; +} + +int +GURL::cgi_arguments(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + return cgi_name_arr.size(); +} + +int +GURL::djvu_cgi_arguments(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int args=0; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + args=cgi_name_arr.size()-(i+1); + break; + } + } + return args; +} + +GUTF8String +GURL::cgi_name(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (num<cgi_name_arr.size())?cgi_name_arr[num]:GUTF8String(); +} + +GUTF8String +GURL::djvu_cgi_name(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GUTF8String arg; + for(int i=0;i<cgi_name_arr.size();i++) + if (cgi_name_arr[i].upcase()==djvuopts) + { + for(i++;i<cgi_name_arr.size();i++) + if (! num--) + { + arg=cgi_name_arr[i]; + break; + } + break; + } + return arg; +} + +GUTF8String +GURL::cgi_value(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (num<cgi_value_arr.size())?cgi_value_arr[num]:GUTF8String(); +} + +GUTF8String +GURL::djvu_cgi_value(int num) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + GUTF8String arg; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + for(i++;i<cgi_name_arr.size();i++) + { + if (! num--) + { + arg=cgi_value_arr[i]; + break; + } + } + break; + } + } + return arg; +} + +DArray<GUTF8String> +GURL::cgi_names(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return cgi_name_arr; +} + +DArray<GUTF8String> +GURL::cgi_values(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return cgi_value_arr; +} + +DArray<GUTF8String> +GURL::djvu_cgi_names(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int i; + DArray<GUTF8String> arr; + for(i=0;(i<cgi_name_arr.size())&& + (cgi_name_arr[i].upcase()!=djvuopts) + ;i++) + EMPTY_LOOP; + + int size=cgi_name_arr.size()-(i+1); + if (size>0) + { + arr.resize(size-1); + for(i=0;i<arr.size();i++) + arr[i]=cgi_name_arr[cgi_name_arr.size()-arr.size()+i]; + } + + return arr; +} + +DArray<GUTF8String> +GURL::djvu_cgi_values(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + + int i; + DArray<GUTF8String> arr; + for(i=0;i<cgi_name_arr.size()&&(cgi_name_arr[i].upcase()!=djvuopts);i++) + EMPTY_LOOP; + + int size=cgi_name_arr.size()-(i+1); + if (size>0) + { + arr.resize(size-1); + for(i=0;i<arr.size();i++) + arr[i]=cgi_value_arr[cgi_value_arr.size()-arr.size()+i]; + } + + return arr; +} + +void +GURL::clear_all_arguments(void) +{ + clear_hash_argument(); + clear_cgi_arguments(); +} + +void +GURL::clear_hash_argument(void) + // Clear anything after first '#' and before the following '?' +{ + if(!validurl) init(); + GCriticalSectionLock lock(&class_lock); + bool found=false; + GUTF8String new_url; + for(const char * start=url;*start;start++) + { + // Break on first CGI arg. + if (*start=='?') + { + new_url+=start; + break; + } + + if (!found) + { + if (*start=='#') + found=true; + else + new_url+=*start; + } + } + url=new_url; +} + +void +GURL::clear_cgi_arguments(void) +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + // Clear the arrays + cgi_name_arr.empty(); + cgi_value_arr.empty(); + + // And clear everything past the '?' sign in the URL + for(const char * ptr=url;*ptr;ptr++) + if (*ptr=='?') + { + url.setat(ptr-url, 0); + break; + } +} + +void +GURL::clear_djvu_cgi_arguments(void) +{ + if(!validurl) init(); + // First - modify the arrays + GCriticalSectionLock lock(&class_lock); + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + cgi_name_arr.resize(i-1); + cgi_value_arr.resize(i-1); + break; + } + } + + // And store them back into the URL + store_cgi_args(); +} + +void +GURL::add_djvu_cgi_argument(const GUTF8String &name, const char * value) +{ + if(!validurl) + init(); + GCriticalSectionLock lock1(&class_lock); + + // Check if we already have the "DJVUOPTS" argument + bool have_djvuopts=false; + for(int i=0;i<cgi_name_arr.size();i++) + { + if (cgi_name_arr[i].upcase()==djvuopts) + { + have_djvuopts=true; + break; + } + } + + // If there is no DJVUOPTS, insert it + if (!have_djvuopts) + { + int pos=cgi_name_arr.size(); + cgi_name_arr.resize(pos); + cgi_value_arr.resize(pos); + cgi_name_arr[pos]=djvuopts; + } + + // Add new argument to the array + int pos=cgi_name_arr.size(); + cgi_name_arr.resize(pos); + cgi_value_arr.resize(pos); + cgi_name_arr[pos]=name; + cgi_value_arr[pos]=value; + + // And update the URL + store_cgi_args(); +} + +bool +GURL::is_local_file_url(void) const +{ + if(!validurl) const_cast<GURL *>(this)->init(); + GCriticalSectionLock lock((GCriticalSection *) &class_lock); + return (protocol()=="file" && url[5]==slash); +} + +static const int +pathname_start(const GUTF8String &url, const int protolength) +{ + const int length=url.length(); + int retval=0; + if(protolength+1<length) + { + retval=url.search(slash,((url[protolength+1] == '/') + ?((url[protolength+2] == '/')?(protolength+3):(protolength+2)) + :(protolength+1))); + } + return (retval>0)?retval:length; +} + +GUTF8String +GURL::pathname(void) const +{ + return (is_local_file_url()) + ?GURL::encode_reserved(UTF8Filename()) + :url.substr(pathname_start(url,protocol().length()),(unsigned int)(-1)); +} + +GURL +GURL::base(void) const +{ + const GUTF8String xurl(get_string()); + const int protocol_length=protocol(xurl).length(); + const int xurl_length=xurl.length(); + const char * const url_ptr=xurl; + const char * ptr, * xslash; + ptr=xslash=url_ptr+protocol_length+1; + if(xslash[0] == '/') + { + xslash++; + if(xslash[0] == '/') + xslash++; + for(ptr=xslash;ptr[0] && !is_argument(ptr);ptr++) + { + if ((ptr[0]==slash)&&ptr[1]&&!is_argument(ptr+1)) + xslash=ptr; + } + if(xslash[0] != '/') + { + xslash=url_ptr+xurl_length; + } + } + return GURL::UTF8( +// ifdef WIN32 +// (*(xslash-1) == colon)? +// (GUTF8String(xurl,(int)(xslash-url_ptr))+"/" ): +// endif + (GUTF8String(xurl,(int)(xslash-url_ptr))+"/")); +} + +bool +GURL::operator==(const GURL & gurl2) const +{ + bool retval=false; + const GUTF8String g1(get_string()); + const int g1_length=g1.length(); + const GUTF8String g2(gurl2.get_string()); + const int g2_length=g2.length(); + if(g1_length == g2_length) // exactly equal + { + retval=(g1==g2); + }else if(g1_length+1 == g2_length) // g1 is g2 with a slash at the end + { + retval=(g2[g1_length] == '/')&&!g1.cmp(g2,g1_length); + }else if(g2_length+1 == g1_length) // g2 is g1 with a slash at the end + { + retval=(g1[g2_length] == '/')&&!g1.cmp(g2,g2_length); + } + return retval; +} + +GUTF8String +GURL::name(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String retval; + if(!is_empty()) + { + const GUTF8String xurl(url); + const int protocol_length=protocol(xurl).length(); + const char * ptr, * xslash=(const char *)xurl+protocol_length-1; + for(ptr=(const char *)xurl+protocol_length; + *ptr && !is_argument(ptr);ptr++) + { + if (*ptr==slash) + xslash=ptr; + } + retval=GUTF8String(xslash+1, ptr-xslash-1); + } + return retval; +} + +GUTF8String +GURL::fname(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + return decode_reserved(name()); +} + +GUTF8String +GURL::extension(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String xfilename=name(); + GUTF8String retval; + + for(int i=xfilename.length()-1;i>=0;i--) + { + if (xfilename[i]=='.') + { + retval=(const char*)xfilename+i+1; + break; + } + } + return retval; +} + +GUTF8String +GURL::decode_reserved(const GUTF8String &gurl) +{ + const char *url=gurl; + char *res; + GPBuffer<char> gres(res,gurl.length()+1); + char *r=res; + for(const char * ptr=url;*ptr;++ptr,++r) + { + if (*ptr!=percent) + { + r[0]=*ptr; + }else + { + int c1,c2; + if ( ((c1=hexval(ptr[1]))>=0) + && ((c2=hexval(ptr[2]))>=0) ) + { + r[0]=(c1<<4)|c2; + ptr+=2; + } else + { + r[0]=*ptr; + } + } + } + r[0]=0; + GUTF8String retval(res); + if(!retval.is_valid()) + { + retval=GNativeString(res); + } + return retval; +} + +GUTF8String +GURL::encode_reserved(const GUTF8String &gs) +{ + const char *s=(const char *)gs; + // Potentially unsafe characters (cf. RFC1738 and RFC1808) + static const char hex[] = "0123456789ABCDEF"; + + unsigned char *retval; + GPBuffer<unsigned char> gd(retval,strlen(s)*3+1); + unsigned char *d=retval; + for (; *s; s++,d++) + { + // Convert directory separator to slashes +#if defined(WIN32) || defined(OS2) + if (*s == backslash || *s== slash) +#else +#ifdef macintosh + if (*s == colon ) +#else +#ifdef UNIX + if (*s == slash ) +#else +#error "Define something here for your operating system" +#endif +#endif +#endif + { + *d = slash; + continue; + } + unsigned char const ss=(unsigned char const)(*s); + // WARNING: Whenever you modify this conversion code, + // make sure, that the following functions are in sync: + // encode_reserved() + // decode_reserved() + // url_to_filename() + // filename_to_url() + // unreserved characters + if ( (ss>='a' && ss<='z') || + (ss>='A' && ss<='Z') || + (ss>='0' && ss<='9') || + (strchr("$-_.+!*'(),:~=", ss)) ) + { + *d = ss; + continue; + } + // escape sequence + d[0] = percent; + d[1] = hex[ (ss >> 4) & 0xf ]; + d[2] = hex[ (ss) & 0xf ]; + d+=2; + } + *d = 0; + return retval; +} + +// ------------------------------------------- +// Functions for converting filenames and urls +// ------------------------------------------- + +static GUTF8String +url_from_UTF8filename(const GUTF8String &gfilename) +{ + if(GURL::UTF8(gfilename).is_valid()) + { + DEBUG_MSG("Debug: URL as Filename: " << gfilename << "\n"); + } + const char *filename=gfilename; + if(filename && (unsigned char)filename[0] == (unsigned char)0xEF + && (unsigned char)filename[1] == (unsigned char)0xBB + && (unsigned char)filename[2] == (unsigned char)0xBF) + { + filename+=3; + } + + // Special case for blank pages + if(!filename || !filename[0]) + { + return GUTF8String(); + } + + // Normalize file name to url slash-and-escape syntax + GUTF8String oname=GURL::expand_name(filename); + GUTF8String nname=GURL::encode_reserved(oname); + + // Preprend "file://" to file name. If file is on the local + // machine, include "localhost". + GUTF8String url=filespecslashes; + const char *cnname=nname; + if (cnname[0] == slash) + { + if (cnname[1] == slash) + { + url += cnname+2; + }else + { + url = localhost + nname; + } + }else + { + url += (localhostspec1+2) + nname; + } + return url; +} + +GUTF8String +GURL::get_string(const bool nothrow) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(nothrow); + return url; +} + +// -- Returns a url for accessing a given file. +// If useragent is not provided, standard url will be created, +// but will not be understood by some versions if IE. +GUTF8String +GURL::get_string(const GUTF8String &useragent) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(); + GUTF8String retval(url); + if(is_local_file_url()&&useragent.length()) + { + if(useragent.search("MSIE") >= 0 || useragent.search("Microsoft")>=0) + { + retval=filespecslashes + expand_name(UTF8Filename()); + } + } + return retval; +} + +GURL::UTF8::UTF8(const GUTF8String &xurl) +: GURL(xurl) {} + +GURL::UTF8::UTF8(const GUTF8String &xurl,const GURL &codebase) +: GURL(xurl,codebase) {} + +GURL::GURL(const GUTF8String &xurl,const GURL &codebase) + : validurl(false) +{ + if(GURL::UTF8(xurl).is_valid()) + { + url=xurl; + }else + { + const char *c=xurl; + if(c[0] == slash) + { + GURL base(codebase); + for(GURL newbase=base.base();newbase!=base;newbase=base.base()) + { + base=newbase; + } + url=base.get_string(true)+GURL::encode_reserved(xurl); + }else + { + url=beautify_path(codebase.get_string(true)+GUTF8String(slash)+GURL::encode_reserved(xurl)); + } + } +} + +GURL::Native::Native(const GNativeString &xurl) +: GURL(xurl) {} + +GURL::Native::Native(const GNativeString &xurl,const GURL &codebase) +: GURL(xurl,codebase) {} + +GURL::GURL(const GNativeString &xurl,const GURL &codebase) + : validurl(false) +{ + GURL retval(xurl.getNative2UTF8(),codebase); + if(retval.is_valid()) + { +#if defined(WIN32) + // Hack for IE to change \\ to / + if(retval.is_local_file_url()) + { + GURL::Filename::UTF8 retval2(retval.UTF8Filename()); + url=retval2.get_string(true); + validurl=false; + }else +#endif // WIN32 + { + url=retval.get_string(true); + validurl=false; + } + } +} + +GURL::Filename::Filename(const GNativeString &gfilename) +{ + url=url_from_UTF8filename(gfilename.getNative2UTF8()); +} + +GURL::Filename::Native::Native(const GNativeString &gfilename) +: GURL::Filename(gfilename) {} + +GURL::Filename::Filename(const GUTF8String &gfilename) +{ + url=url_from_UTF8filename(gfilename); +} + +GURL::Filename::UTF8::UTF8(const GUTF8String &gfilename) +: GURL::Filename(gfilename) {} + +// filename -- +// -- Applies heuristic rules to convert a url into a valid file name. +// Returns a simple basename in case of failure. +GUTF8String +GURL::UTF8Filename(void) const +{ + GUTF8String retval; + if(! is_empty()) + { + const char *url_ptr=url; + + // WARNING: Whenever you modify this conversion code, + // make sure, that the following functions are in sync: + // encode_reserved() + // decode_reserved() + // url_to_filename() + // filename_to_url() + + GUTF8String urlcopy=decode_reserved(url); + url_ptr = urlcopy; + + // All file urls are expected to start with filespec which is "file:" + if (GStringRep::cmp(filespec, url_ptr, sizeof(filespec)-1)) //if not + return GOS::basename(url_ptr); + url_ptr += sizeof(filespec)-1; + +#if defined(macintosh) + //remove all leading slashes + for(;*url_ptr==slash;url_ptr++) + EMPTY_LOOP; + // Remove possible localhost spec + if ( !GStringRep::cmp(localhost, url_ptr, sizeof(localhost)-1) ) + url_ptr += sizeof(localhost)-1; + //remove all leading slashes + while(*url_ptr==slash) + url_ptr++; +#else + // Remove possible localhost spec + if ( !GStringRep::cmp(localhostspec1, url_ptr, sizeof(localhostspec1)-1) ) + // RFC 1738 local host form + url_ptr += sizeof(localhostspec1)-1; + else if ( !GStringRep::cmp(localhostspec2, url_ptr, sizeof(localhostspec2)-1 ) ) + // RFC 1738 local host form + url_ptr += sizeof(localhostspec2)-1; + else if ( (strlen(url_ptr) > 4) // "file://<letter>:/<path>" + && (url_ptr[0] == slash) // "file://<letter>|/<path>" + && (url_ptr[1] == slash) + && isalpha(url_ptr[2]) + && ( url_ptr[3] == colon || url_ptr[3] == '|' ) + && (url_ptr[4] == slash) ) + url_ptr += 2; + else if ( (strlen(url_ptr)) > 2 // "file:/<path>" + && (url_ptr[0] == slash) + && (url_ptr[1] != slash) ) + url_ptr++; +#endif + + // Check if we are finished +#if defined(macintosh) + { + char *l_url; + GPBuffer<char> gl_url(l_url,strlen(url_ptr)+1); + const char *s; + char *r; + for ( s=url_ptr,r=l_url; *s; s++,r++) + { + *r=(*s == slash)?colon:*s; + } + *r=0; + retval = expand_name(l_url,root); + } +#else + retval = expand_name(url_ptr,root); +#endif + +#if defined(WIN32) || defined(OS2) + if (url_ptr[0] && url_ptr[1]=='|' && url_ptr[2]== slash) + { + if ((url_ptr[0]>='a' && url_ptr[0]<='z') + || (url_ptr[0]>='A' && url_ptr[0]<='Z')) + { + GUTF8String drive; + drive.format("%c%c%c", url_ptr[0],colon,backslash); + retval = expand_name(url_ptr+3, drive); + } + } +#endif + } + // Return what we have + return retval; +} + +GNativeString +GURL::NativeFilename(void) const +{ + return UTF8Filename().getUTF82Native(); +} + +#if defined(UNIX) || defined(macintosh) || defined(OS2) +static int +urlstat(const GURL &url,struct stat &buf) +{ + return ::stat(url.NativeFilename(),&buf); +} +#endif + +// is_file(url) -- +// -- returns true if filename denotes a regular file. +bool +GURL::is_file(void) const +{ + bool retval=false; + if(is_local_file_url()) + { +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + if (!urlstat(*this,buf)) + { + retval=!(buf.st_mode & S_IFDIR); + } +#elif defined(WIN32) + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=!( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +bool +GURL::is_local_path(void) const +{ + bool retval=false; + if(is_local_file_url()) + { +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + retval=!urlstat(*this,buf); +#else + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=( (dwAttrib|1) != 0xFFFFFFFF); +#endif + } + return retval; +} + +// is_dir(url) -- +// -- returns true if url denotes a directory. +bool +GURL::is_dir(void) const +{ + bool retval=false; + if(is_local_file_url()) + { + // UNIX implementation +#if defined(UNIX) || defined(macintosh) || defined(OS2) + struct stat buf; + if (!urlstat(*this,buf)) + { + retval=(buf.st_mode & S_IFDIR); + } +#elif defined(WIN32) // (either Windows or WCE) + GUTF8String filename(UTF8Filename()); + if(filename.length() >= MAX_PATH) + { + if(!filename.cmp("\\\\",2)) + filename="\\\\?\\UNC"+filename.substr(1,-1); + else + filename="\\\\?\\"+filename; + } + wchar_t *wfilename; + const size_t wfilename_size=filename.length()+1; + GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size); + filename.ncopy(wfilename,wfilename_size); + DWORD dwAttrib; + dwAttrib = GetFileAttributesW(wfilename); + if((dwAttrib|1) == 0xFFFFFFFF) + { + USES_CONVERSION ; + dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt + } + retval=((dwAttrib != 0xFFFFFFFF)&&( dwAttrib & FILE_ATTRIBUTE_DIRECTORY )); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +// Follows symbolic links. +GURL +GURL::follow_symlinks(void) const +{ + GURL ret = *this; +#if defined(S_IFLNK) +#if defined(UNIX) || defined(macintosh) + int lnklen; + char lnkbuf[MAXPATHLEN+1]; + struct stat buf; + while ( (urlstat(ret, buf) >= 0) && + (buf.st_mode & S_IFLNK) && + ((lnklen = readlink(ret.NativeFilename(),lnkbuf,sizeof(lnkbuf))) > 0) ) + { + lnkbuf[lnklen] = 0; + GNativeString lnk(lnkbuf); + ret = GURL(lnk, ret.base()); + } +#endif +#endif + return ret; +} + +int +GURL::mkdir() const +{ + if(! is_local_file_url()) + return -1; + int retval=0; + const GURL baseURL=base(); + if (baseURL.get_string() != url && !baseURL.is_dir()) + retval = baseURL.mkdir(); + if(!retval) + { +#if defined(UNIX) + if (is_dir()) + retval = 0; + else + retval = ::mkdir(NativeFilename(), 0755); +#elif defined(WIN32) + USES_CONVERSION; + if (is_dir()) + retval = 0; + else + retval = CreateDirectory(A2CT(NativeFilename()), NULL); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +// deletefile +// -- deletes a file or directory + +int +GURL::deletefile(void) const +{ + int retval = -1; + if(is_local_file_url()) + { +#if defined(UNIX) + if (is_dir()) + retval = ::rmdir(NativeFilename()); + else + retval = ::unlink(NativeFilename()); +#elif defined(WIN32) + USES_CONVERSION; + if (is_dir()) + retval = ::RemoveDirectory(A2CT(NativeFilename())); + else + retval = ::DeleteFile(A2CT(NativeFilename())); +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +GList<GURL> +GURL::listdir(void) const +{ + GList<GURL> retval; + if(is_dir()) + { +#if defined(UNIX) || defined(OS2) + DIR * dir=opendir(NativeFilename());//MBCS cvt + for(dirent *de=readdir(dir);de;de=readdir(dir)) + { + const int len = NAMLEN(de); + if (de->d_name[0]== dot && len==1) + continue; + if (de->d_name[0]== dot && de->d_name[1]== dot && len==2) + continue; + retval.append(GURL::Native(de->d_name,*this)); + } + closedir(dir); +#elif defined (WIN32) + GURL::UTF8 wildcard("*.*",*this); + WIN32_FIND_DATA finddata; + HANDLE handle = FindFirstFile(wildcard.NativeFilename(), &finddata);//MBCS cvt + const GUTF8String gpathname=pathname(); + const GUTF8String gbase=base().pathname(); + if( handle != INVALID_HANDLE_VALUE) + { + do + { + GURL::UTF8 Entry(finddata.cFileName,*this); + const GUTF8String gentry=Entry.pathname(); + if((gentry != gpathname) && (gentry != gbase)) + retval.append(Entry); + } while( FindNextFile(handle, &finddata) ); + + FindClose(handle); + } +#else +# error "Define something here for your operating system" +#endif + } + return retval; +} + +int +GURL::cleardir(const int timeout) const +{ + int retval=(-1); + if(is_dir()) + { + GList<GURL> dirlist=listdir(); + retval=0; + for(GPosition pos=dirlist;pos&&!retval;++pos) + { + const GURL &Entry=dirlist[pos]; + if(Entry.is_dir()) + { + if((retval=Entry.cleardir(timeout)) < 0) + { + break; + } + } + if(((retval=Entry.deletefile())<0) && (timeout>0)) + { + GOS::sleep(timeout); + retval=Entry.deletefile(); + } + } + } + return retval; +} + +int +GURL::renameto(const GURL &newurl) const +{ + if (is_local_file_url() && newurl.is_local_file_url()) + return rename(NativeFilename(),newurl.NativeFilename()); + return -1; +} + +// expand_name(filename[, fromdirname]) +// -- returns the full path name of filename interpreted +// relative to fromdirname. Use current working dir when +// fromdirname is null. +GUTF8String +GURL::expand_name(const GUTF8String &xfname, const char *from) +{ + const char *fname=xfname; + GUTF8String retval; + const size_t maxlen=xfname.length()*9+MAXPATHLEN+10; + char * const string_buffer = retval.getbuf(maxlen); + // UNIX implementation +#if defined(UNIX) + // Perform tilde expansion + GUTF8String senv; + if (fname && fname[0]==tilde) + { + int n; + for(n=1;fname[n] && fname[n]!= slash;n++) + EMPTY_LOOP; + struct passwd *pw=0; + if (n!=1) + { + GUTF8String user(fname+1, n-1); + pw=getpwnam(user); + }else if ((senv=GOS::getenv("HOME")).length()) + { + from=(const char *)senv; + fname = fname + n; + }else if ((senv=GOS::getenv("LOGNAME")).length()) + { + pw = getpwnam((const char *)senv.getUTF82Native()); + }else + { + pw=getpwuid(getuid()); + } + if (pw) + { + senv=GNativeString(pw->pw_dir).getNative2UTF8(); + from = (const char *)senv; + fname = fname + n; + } + for(;fname[0] == slash; fname++) + EMPTY_LOOP; + } + // Process absolute vs. relative path + if (fname && fname[0]== slash) + { + string_buffer[0]=slash; + string_buffer[1]=0; + }else if (from) + { + strcpy(string_buffer, expand_name(from)); + }else + { + strcpy(string_buffer, GOS::cwd()); + } + char *s = string_buffer + strlen(string_buffer); + if(fname) + { + for(;fname[0]== slash;fname++) + EMPTY_LOOP; + // Process path components + while(fname[0]) + { + if (fname[0] == dot ) + { + if (!fname[1] || fname[1]== slash) + { + fname++; + continue; + }else if (fname[1]== dot && (fname[2]== slash || !fname[2])) + { + fname +=2; + for(;s>string_buffer+1 && *(s-1)== slash; s--) + EMPTY_LOOP; + for(;s>string_buffer+1 && *(s-1)!= slash; s--) + EMPTY_LOOP; + continue; + } + } + if ((s==string_buffer)||(*(s-1)!= slash)) + { + *s = slash; + s++; + } + while (*fname &&(*fname!= slash)) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + { + G_THROW( ERR_MSG("GURL.big_name") ); + } + } + *s = 0; + for(;fname[0]== slash;fname++) + EMPTY_LOOP; + } + } + if (!fname || !fname[0]) + { + for(;s>string_buffer+1 && *(s-1) == slash; s--) + EMPTY_LOOP; + *s = 0; + } +#elif defined (WIN32) // WIN32 implementation + // Handle base + strcpy(string_buffer, (char const *)(from ? expand_name(from) : GOS::cwd())); + // GNativeString native; + if (fname) + { + char *s = string_buffer; + char drv[4]; + // Handle absolute part of fname + // Put absolute part of the file name in string_buffer, and + // the relative part pointed to by fname. + if (fname[0]== slash || fname[0]== backslash) + { + if (fname[1]== slash || fname[1]== backslash) + { // Case "//abcd" + s[0]=s[1]= backslash; s[2]=0; + } + else + { // Case "/abcd" or "/" + // File is at the root of the current drive. Delete the + // slash at the beginning of the filename and leave + // an explicit identification of the root of the drive in + // string_buffer. + fname++; + s[3] = '\0'; + } + } + else if (fname[0] && fname[1]==colon) + { + if (fname[2]!= slash && fname[2]!= backslash) + { // Case "x:abcd" + if ( toupper((unsigned char)s[0]) != toupper((unsigned char)fname[0]) + || s[1]!=colon) + { + drv[0]=fname[0]; + drv[1]=colon; + drv[2]= dot ; + drv[3]=0; + GetFullPathName(drv, maxlen, string_buffer, &s); + strcpy(string_buffer,(const char *)GUTF8String(string_buffer).getNative2UTF8()); + s = string_buffer; + } + fname += 2; + } + else if (fname[3]!= slash && fname[3]!= backslash) + { // Case "x:/abcd" + s[0]=toupper((unsigned char)fname[0]); + s[1]=colon; + s[2]=backslash; + s[3]=0; + fname += 3; + } + else + { // Case "x://abcd" + s[0]=s[1]=backslash; + s[2]=0; + fname += 4; + } + } + // Process path components + for(;*fname== slash || *fname==backslash;fname++) + EMPTY_LOOP; + while(*fname) + { + if (fname[0]== dot ) + { + if (fname[1]== slash || fname[1]==backslash || !fname[1]) + { + fname++; + continue; + }else if ((fname[1] == dot) + && (fname[2]== slash || fname[2]==backslash || !fname[2])) + { + fname += 2; + char *back=_tcsrchr(string_buffer,backslash); + char *forward=_tcsrchr(string_buffer,slash); + if(back>forward) + { + *back=0; + }else if(forward) + { + *forward=0; + } + s = string_buffer; + continue; + } + char* s2=s;//MBCS DBCS + for(;*s;s++) + EMPTY_LOOP; + char* back = _tcsrchr(s2,backslash);//MBCS DBCS + if ((s>string_buffer)&&(*(s-1)!= slash)&& + (back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS + { + *s = backslash; + s++; + } + while (*fname && *fname!= slash && *fname!=backslash) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + } + char* s2=s;//MBCS DBCS + for(;*s;s++) + EMPTY_LOOP; + char* back = _tcsrchr(s2,backslash);//MBCS DBCS + if ((s>string_buffer)&&(*(s-1)!= slash) + &&(back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS + { + *s = backslash; + s++; + } + while (*fname && (*fname!= slash) && (*fname!=backslash)) + { + *s = *fname++; + if ((size_t)((++s)-string_buffer) > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + for(;(*fname== slash)||(*fname==backslash);fname++) + EMPTY_LOOP; + } + } +#elif defined(macintosh) // MACINTOSH implementation + strcpy(string_buffer, (const char *)(from?from:GOS::cwd())); + + if (!GStringRep::cmp(fname, string_buffer,strlen(string_buffer)) || is_file(fname)) + { + strcpy(string_buffer, "");//please don't expand, the logic of filename is chaos. + } + + // Process path components + char *s = string_buffer + strlen(string_buffer); + if(fname) + { + for(;fname[0]==colon;fname++) + EMPTY_LOOP; + while(fname[0]) + { + if (fname[0]== dot ) + { + if (fname[1]==colon || !fname[1]) + { + fname++; + continue; + } + if ((fname[1]== dot ) + &&(fname[2]==colon || fname[2]==0)) + { + fname +=2; + for(;(s>string_buffer+1)&&(*(s-1)==colon);s--) + EMPTY_LOOP; + for(;(s>string_buffer+1)&&(*(s-1)!=colon);s--) + EMPTY_LOOP; + continue; + } + } + if ((s==string_buffer)||(*(s-1)!=colon)) + { + *s = colon; + s++; + } + while (*fname!=0 && *fname!=colon) + { + *s = *fname++; + if ((++s)-string_buffer > maxlen) + G_THROW( ERR_MSG("GURL.big_name") ); + } + *s = 0; + for(;fname[0]==colon;fname++) + EMPTY_LOOP; + } + } + for(;(s>string_buffer+1) && (*(s-1)==colon);s--) + EMPTY_LOOP; + *s = 0; + return ((string_buffer[0]==colon)?(string_buffer+1):string_buffer); +#else +# error "Define something here for your operating system" +#endif + return retval; +} + +unsigned int +hash(const GURL & gurl) +{ + unsigned int retval; + const GUTF8String s(gurl.get_string()); + const int len=s.length(); + if(len && (s[len-1] == '/')) // Don't include the trailing slash as part of the hash. + { + retval=hash(s.substr(0,len-1)); + }else + { + retval=hash(s); + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GURL.h b/kviewshell/plugins/djvu/libdjvu/GURL.h new file mode 100644 index 00000000..eb3ed4bc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GURL.h @@ -0,0 +1,446 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GURL.h,v 1.9 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _GURL_H_ +#define _GURL_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GString.h" +#include "Arrays.h" +#include "GThreads.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name GURL.h + Files #"GURL.h"# and #"GURL.cpp"# contain the implementation of the + \Ref{GURL} class used to store URLs in a system independent format. + @memo System independent URL representation. + @author Andrei Erofeev <[email protected]> + +// From: Leon Bottou, 1/31/2002 +// This has been heavily changed by Lizardtech. +// They decided to use URLs for everyting, including +// the most basic file access. The URL class now is a unholy +// mixture of code for syntactically parsing the urls (which is was) +// and file status code (only for local file: urls). + + @version #$Id: GURL.h,v 1.9 2003/11/07 22:08:21 leonb Exp $# +*/ + +//@{ + +/** System independent URL representation. + + This class is used in the library to store URLs in a system independent + format. The idea to use a general class to hold URL arose after we + realized, that DjVu had to be able to access files both from the WEB + and from the local disk. While it is strange to talk about system + independence of HTTP URLs, file names formats obviously differ from + platform to platform. They may contain forward slashes, backward slashes, + colons as separators, etc. There maybe more than one URL corresponding + to the same file name. Compare #file:/dir/file.djvu# and + #file://localhost/dir/file.djvu#. + + To simplify a developer's life we have created this class, which contains + inside a canonical representation of URLs. + + File URLs are converted to internal format with the help of \Ref{GOS} class. + + All other URLs are modified to contain only forward slashes. +*/ + +class GURL +{ +public: + class Filename; + class UTF8; + class Native; +protected: + /** @name Constructors + Accept the string URL, check that it starts from #file:/# + or #http:/# and convert to internal system independent + representation. + */ + //@{ + /// + GURL(const char * url_string); + //@} + +public: + GURL(void); + + GURL(const GUTF8String & url_string); + + GURL(const GNativeString & url_string); + + GURL(const GUTF8String &xurl, const GURL &codebase); + + GURL(const GNativeString &xurl, const GURL &codebase); + + /// Copy constructor + GURL(const GURL & gurl); + + /// The destructor + virtual ~GURL(void) {} + +private: + // The 'class_lock' should be locked whenever you're accessing + // url, or cgi_name_arr, or cgi_value_arr. + GCriticalSection class_lock; +protected: + GUTF8String url; + DArray<GUTF8String> cgi_name_arr, cgi_value_arr; + bool validurl; + + void init(const bool nothrow=false); + void convert_slashes(void); + void beautify_path(void); + static GUTF8String beautify_path(GUTF8String url); + + static GUTF8String protocol(const GUTF8String& url); + void parse_cgi_args(void); + void store_cgi_args(void); +public: + /// Test if the URL is valid. If invalid, reinitialize. + bool is_valid(void) const; // const lies to the compiler because of dependency problems + + /// Extracts the {\em protocol} part from the URL and returns it + GUTF8String protocol(void) const; + + /** Returns string after the first '\#' with decoded + escape sequences. */ + GUTF8String hash_argument(void) const; + + /** Inserts the #arg# after a separating hash into the URL. + The function encodes any illegal character in #arg# using + \Ref{GOS::encode_reserved}(). */ + void set_hash_argument(const GUTF8String &arg); + + /** Returns the total number of CGI arguments in the URL. + CGI arguments follow '#?#' sign and are separated by '#&#' signs */ + int cgi_arguments(void) const; + + /** Returns the total number of DjVu-related CGI arguments (arguments + following #DJVUOPTS# in the URL). */ + int djvu_cgi_arguments(void) const; + + /** Returns that part of CGI argument number #num#, which is + before the equal sign. */ + GUTF8String cgi_name(int num) const; + + /** Returns that part of DjVu-related CGI argument number #num#, + which is before the equal sign. */ + GUTF8String djvu_cgi_name(int num) const; + + /** Returns that part of CGI argument number #num#, which is + after the equal sign. */ + GUTF8String cgi_value(int num) const; + + /** Returns that part of DjVu-related CGI argument number #num#, + which is after the equal sign. */ + GUTF8String djvu_cgi_value(int num) const; + + /** Returns array of all known CGI names (part of CGI argument before + the equal sign) */ + DArray<GUTF8String>cgi_names(void) const; + + /** Returns array of names of DjVu-related CGI arguments (arguments + following #DJVUOPTS# option. */ + DArray<GUTF8String>djvu_cgi_names(void) const; + + /** Returns array of all known CGI names (part of CGI argument before + the equal sign) */ + DArray<GUTF8String>cgi_values(void) const; + + /** Returns array of values of DjVu-related CGI arguments (arguments + following #DJVUOPTS# option. */ + DArray<GUTF8String>djvu_cgi_values(void) const; + + /// Erases everything after the first '\#' or '?' + void clear_all_arguments(void); + + /// Erases everything after the first '\#' + void clear_hash_argument(void); + + /// Erases DjVu CGI arguments (following "#DJVUOPTS#") + void clear_djvu_cgi_arguments(void); + + /// Erases all CGI arguments (following the first '?') + void clear_cgi_arguments(void); + + /** Appends the specified CGI argument. Will insert "#DJVUOPTS#" if + necessary */ + void add_djvu_cgi_argument(const GUTF8String &name, const char * value=0); + + /** Returns the URL corresponding to the directory containing + the document with this URL. The function basically takes the + URL and clears everything after the last slash. */ + GURL base(void) const; + + /// Returns the aboslute URL without the host part. + GUTF8String pathname(void) const; + + /** Returns the name part of this URL. + For example, if the URL is #http://www.lizardtech.com/file%201.djvu# then + this function will return #file%201.djvu#. \Ref{fname}() will + return #file 1.djvu# at the same time. */ + GUTF8String name(void) const; + + /** Returns the name part of this URL with escape sequences expanded. + For example, if the URL is #http://www.lizardtech.com/file%201.djvu# then + this function will return #file 1.djvu#. \Ref{name}() will + return #file%201.djvu# at the same time. */ + GUTF8String fname(void) const; + + /// Returns the extention part of name of document in this URL. + GUTF8String extension(void) const; + + /// Checks if this is an empty URL + bool is_empty(void) const; + + /// Checks if the URL is local (starts from #file:/#) or not + bool is_local_file_url(void) const; + + /** @name Concatenation operators + Concatenate the GURL with the passed {\em name}. If the {\em name} + is absolute (has non empty protocol prefix), we just return + #GURL(name)#. Otherwise the #name# is appended to the GURL after a + separating slash. + */ + //@{ + /// +// GURL operator+(const GUTF8String &name) const; + //@} + + /// Returns TRUE if #gurl1# and #gurl2# are the same + bool operator==(const GURL & gurl2) const; + + /// Returns TRUE if #gurl1# and #gurl2# are different + bool operator!=(const GURL & gurl2) const; + + /// Assignment operator + GURL & operator=(const GURL & url); + + /// Returns Internal URL representation + operator const char*(void) const { return url; }; + + /** Returns a string representing the URL. This function normally + returns a standard file URL as described in RFC 1738. + Some versions of MSIE do not support this standard syntax. + A brain damaged MSIE compatible syntax is generated + when the optional argument #useragent# contains string #"MSIE"# or + #"Microsoft"#. */ + GUTF8String get_string(const GUTF8String &useragent) const; + + GUTF8String get_string(const bool nothrow=false) const; + + /// Escape special characters + static GUTF8String encode_reserved(const GUTF8String &gs); + + /** Decodes reserved characters from the URL. + See also: \Ref{encode_reserved}(). */ + static GUTF8String decode_reserved(const GUTF8String &url); + + /// Test if this url is an existing file, directory, or device. + bool is_local_path(void) const; + + /// Test if this url is an existing file. + bool is_file(void) const; + + /// Test if this url is an existing directory. + bool is_dir(void) const; + + /// Follows symbolic links. + GURL follow_symlinks(void) const; + + /// Creates the specified directory. + int mkdir(void) const; + + /** Deletes file or directory. + Directories are not deleted unless the directory is empty. + Returns a negative number if an error occurs. */ + int deletefile(void) const; + + /** Recursively erases contents of directory. The directory + itself will not be removed. */ + int cleardir(const int timeout=0) const; + + /// Rename a file or directory. + int renameto(const GURL &newurl) const; + + /// List the contents of a directory. + GList<GURL> listdir(void) const; + + /** Returns a filename for a URL. Argument #url# must be a legal file URL. + This function applies heuristic rules to convert the URL into a valid + file name. It is guaranteed that this function can properly parse all + URLs generated by #filename_to_url#. The heuristics also work better when + the file actually exists. The empty string is returned when this + function cannot parse the URL or when the URL is not a file URL. + URL formats are as described in RFC 1738 plus the following alternative + formats for files on the local host: + + file://<letter>:/<path> + file://<letter>|/<path> + file:/<path> + + which are accepted because various browsers recognize them.*/ + GUTF8String UTF8Filename(void) const; + /// Same but returns a native string. + GNativeString NativeFilename(void) const; + + /** Hashing function. + @return hash suitable for usage in \Ref{GMap} */ + friend unsigned int hash(const GURL & gurl); + + /** Returns fully qualified file names. This functions constructs the fully + qualified name of file or directory #filename#. When provided, the + optional argument #fromdirname# is used as the current directory when + interpreting relative specifications in #filename#. Function + #expand_name# is very useful for logically concatenating file names. It + knows which separators should be used for each operating system and it + knows which syntactical rules apply. */ + static GUTF8String expand_name(const GUTF8String &filename, const char *fromdirname=0); +}; + +class GURL::UTF8 : public GURL +{ +public: + UTF8(const GUTF8String &xurl); + UTF8(const GUTF8String &xurl, const GURL &codebase); +}; + +class GURL::Native : public GURL +{ +public: + Native(const GNativeString &xurl); + Native(const GNativeString &xurl, const GURL &codebase); +}; + +class GURL::Filename : public GURL +{ +public: + Filename(const GUTF8String &filename); + Filename(const GNativeString &filename); + class UTF8; + class Native; +}; + +class GURL::Filename::UTF8 : public GURL::Filename +{ +public: + UTF8(const GUTF8String &filename); +}; + +class GURL::Filename::Native : public GURL::Filename +{ +public: + Native(const GNativeString &filename); +}; + + +inline bool +GURL::operator!=(const GURL & gurl2) const +{ + return !(*this == gurl2); +} + +inline GUTF8String +GURL::protocol(void) const +{ + return protocol(get_string()); +} + +inline bool +GURL::is_empty(void) const +{ + return !url.length()||!get_string().length(); +} + +// Test if the URL is valid. +// If invalid, reinitialize and return the result. +inline bool +GURL::is_valid(void) const +{ + if(!validurl) + const_cast<GURL *>(this)->init(true); + return validurl; +} + + + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp b/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp new file mode 100644 index 00000000..dbbefc5c --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GUnicode.cpp @@ -0,0 +1,790 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: GUnicode.cpp,v 1.11 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "GString.h" +#if HAS_ICONV +#include <iconv.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static unsigned char nill=0; + +static void const * +checkmarks(void const * const xbuf, + unsigned int &bufsize, + GStringRep::EncodeType &rep) +{ + unsigned char const *buf=(unsigned char const *)xbuf; + if(bufsize >= 2 || (xbuf && !bufsize && rep != GStringRep::XOTHER)) + { + const unsigned int s=(((unsigned int)buf[0])<<8)+(unsigned int)buf[1]; + switch(s) + { + case 0: + if((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4BE) + ||(!bufsize && rep == GStringRep::XUCS4_2143)) + { + const unsigned int s=(((unsigned int)buf[2])<<8)+(unsigned int)buf[3]; + if(s == 0xfeff) + { + rep=GStringRep::XUCS4BE; + buf+=4; + }else if(s == 0xfffe) + { + rep=GStringRep::XUCS4_2143; + buf+=4; + } + } + break; + case 0xfffe: + if(((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4LE)) + && !((unsigned char *)buf)[2] && !((unsigned char *)buf)[3]) + { + rep=GStringRep::XUCS4LE; + buf+=4; + }else + { + rep=GStringRep::XUTF16LE; + buf+=2; + } + break; + case 0xfeff: + if(((bufsize>=4)||(!bufsize && rep == GStringRep::XUCS4_3412)) + && !((unsigned char *)buf)[2] && !((unsigned char *)buf)[3]) + { + rep=GStringRep::XUCS4_3412; + buf+=4; + }else + { + rep=GStringRep::XUTF16LE; + buf+=2; + } + break; + case 0xefbb: + if(((bufsize>=3)||(!bufsize && GStringRep::XUTF8 == rep))&&(buf[2] == 0xbf)) + { + rep=GStringRep::XUTF8; + buf+=3; + } + break; + default: + break; + } + } + if(buf != xbuf) + { + if(bufsize) + { + const size_t s=(size_t)xbuf-(size_t)buf; + if(bufsize> s) + { + bufsize-=s; + }else + { + bufsize=0; + buf=(const unsigned char *)&nill; + } + } + } + return buf; +} + +class GStringRep::Unicode : public GStringRep::UTF8 +{ +public: + GP<GStringRep> encoding; + EncodeType encodetype; + void *remainder; + GPBufferBase gremainder; +public: + Unicode(void); + /// virtual destructor. + virtual ~Unicode(); + + static GP<GStringRep> create(const unsigned int sz); + static GP<GStringRep> create(void const * const buf, unsigned int bufsize, + const EncodeType, const GP<GStringRep> &encoding); + static GP<GStringRep> create( void const * const buf, + unsigned int size, const EncodeType encodetype ); + static GP<GStringRep> create( void const * const buf, + const unsigned int size, GP<GStringRep> encoding ); + static GP<GStringRep> create( void const * const buf, + const unsigned int size, const GP<Unicode> &remainder ); + +protected: + virtual void set_remainder( void const * const buf, const unsigned int size, + const EncodeType encodetype ); + virtual void set_remainder( void const * const buf, const unsigned int size, + const GP<GStringRep> &encoding ); + virtual void set_remainder( const GP<Unicode> &remainder ); + virtual GP<Unicode> get_remainder(void) const; +}; +// static unsigned long UTF8toUCS4(unsigned char const *&,void const * const); +static unsigned long xUTF16toUCS4(unsigned short const *&s,void const * const); +static unsigned long UTF16BEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UTF16LEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4BEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4LEtoUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4_3412toUCS4(unsigned char const *&s,void const * const); +static unsigned long UCS4_2143toUCS4(unsigned char const *&s,void const * const); + +GP<GStringRep> +GStringRep::Unicode::create(const unsigned int sz) +{ + GP<GStringRep> gaddr; + if (sz > 0) + { + GStringRep *addr; + gaddr=(addr=new GStringRep::Unicode); + addr->data=(char *)(::operator new(sz+1)); + addr->size = sz; + addr->data[sz] = 0; + } + return gaddr; +} + +GStringRep::Unicode::Unicode(void) +: encodetype(XUTF8), gremainder(remainder,0,1) {} + +GStringRep::Unicode::~Unicode() {} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + const EncodeType t, + const GP<GStringRep> &encoding) +{ + return (encoding->size) + ?create(xbuf,bufsize,encoding) + :create(xbuf,bufsize,t); +} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + const unsigned int bufsize, + const GP<Unicode> &xremainder ) +{ + Unicode *r=xremainder; + GP<GStringRep> retval; + if(r) + { + const int s=r->gremainder; + if(xbuf && bufsize) + { + if(s) + { + void *buf; + GPBufferBase gbuf(buf,s+bufsize,1); + memcpy(buf,r->remainder,s); + memcpy((void *)((size_t)buf+s),xbuf,bufsize); + retval=((r->encoding) + ?create(buf,s+bufsize,r->encoding) + :create(buf,s+bufsize,r->encodetype)); + }else + { + retval=((r->encoding) + ?create(xbuf,bufsize,r->encoding) + :create(xbuf,bufsize,r->encodetype)); + } + }else if(s) + { + void *buf; + GPBufferBase gbuf(buf,s,1); + memcpy(buf,r->remainder,s); + retval=((r->encoding) + ?create(buf,s,r->encoding) + :create(buf,s,r->encodetype)); + }else + { + retval=((r->encoding) + ?create(0,0,r->encoding) + :create(0,0,r->encodetype)); + } + }else + { + retval=create(xbuf,bufsize,XUTF8); + } + return retval; +} + +#if HAS_ICONV +/* This template works around incompatible iconv protoypes */ +template<typename _T> inline size_t +iconv_adaptor(size_t(*iconv_func)(iconv_t, _T, size_t *, char**, size_t*), + iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + return iconv_func (cd, (_T)inbuf, inbytesleft, outbuf, outbytesleft); +} +#endif + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + GP<GStringRep> encoding) +{ + GP<GStringRep> retval; + GStringRep *e=encoding; + if(e) + { + e=(encoding=e->upcase()); + } + if(!e || !e->size) + { + retval=create(xbuf,bufsize,XOTHER); + }else if(!e->cmp("UTF8") || !e->cmp("UTF-8")) + { + retval=create(xbuf,bufsize,XUTF8); + }else if(!e->cmp("UTF16")|| !e->cmp("UTF-16") + || !e->cmp("UCS2") || !e->cmp("UCS2")) + { + retval=create(xbuf,bufsize,XUTF16); + }else if(!e->cmp("UCS4") || !e->cmp("UCS-4")) + { + retval=create(xbuf,bufsize,XUCS4); + }else + { +#if HAS_ICONV + EncodeType t=XOTHER; + void const * const buf=checkmarks(xbuf,bufsize,t); + if(t != XOTHER) + { + retval=create(xbuf,bufsize,t); + }else if(buf && bufsize) + { + unsigned char const *eptr=(unsigned char *)buf; + unsigned int j=0; + for(j=0;(j<bufsize)&&*eptr;j++,eptr++) + EMPTY_LOOP; + if (j) + { + unsigned char const *ptr=(unsigned char *)buf; + if(e) + { + iconv_t cv=iconv_open("UTF-8",(const char *)e); + if(cv == (iconv_t)(-1)) + { + const int i=e->search('-'); + if(i>=0) + { + cv=iconv_open("UTF-8",e->data+i+1); + } + } + if(cv == (iconv_t)(-1)) + { + retval=create(0,0,XOTHER); + }else + { + size_t ptrleft=(eptr-ptr); + char *utf8buf; + size_t pleft=6*ptrleft+1; + GPBuffer<char> gutf8buf(utf8buf,pleft); + char *p=utf8buf; + unsigned char const *last=ptr; + for(;iconv_adaptor(iconv, cv, (char**)&ptr, &ptrleft, &p, &pleft);last=ptr) + EMPTY_LOOP; + iconv_close(cv); + retval=create(utf8buf,(size_t)last-(size_t)buf,t); + retval->set_remainder(last,(size_t)eptr-(size_t)last,e); + } + } + }else + { + retval=create(0,0,XOTHER); + retval->set_remainder(0,0,e); + } + } +#else + retval=create(xbuf,bufsize,XOTHER); +#endif + } + return retval; +} + +GP<GStringRep> +GStringRep::Unicode::create( + void const * const xbuf, + unsigned int bufsize, + EncodeType t) +{ + GP<GStringRep> gretval; + GStringRep *retval=0; + void const * const buf=checkmarks(xbuf,bufsize,t); + if(buf && bufsize) + { + unsigned char const *eptr=(unsigned char *)buf; + unsigned int maxutf8size=0; + void const* const xeptr=(void const *)((size_t)eptr+bufsize); + switch(t) + { + case XUCS4: + case XUCS4BE: + case XUCS4LE: + case XUCS4_2143: + case XUCS4_3412: + { + for(unsigned long w; + (eptr<xeptr)&&(w=*(unsigned long const *)eptr); + eptr+=sizeof(unsigned long)) + { + maxutf8size+=(w>0x7f)?6:1; + } + break; + } + case XUTF16: + case XUTF16BE: + case XUTF16LE: + { + for(unsigned short w; + (eptr<xeptr)&&(w=*(unsigned short const *)eptr); + eptr+=sizeof(unsigned short)) + { + maxutf8size+=3; + } + break; + } + case XUTF8: + for(;(eptr<xeptr)&&*eptr;maxutf8size++,eptr++) + EMPTY_LOOP; + break; + case XEBCDIC: + for(;(eptr<xeptr)&&*eptr;eptr++) + { + maxutf8size+=(*eptr>0x7f)?2:1; + } + break; + default: + break; + } + unsigned char *utf8buf=0; + GPBuffer<unsigned char> gutf8buf(utf8buf,maxutf8size+1); + utf8buf[0]=0; + if (maxutf8size) + { + unsigned char *optr=utf8buf; + int len=0; + unsigned char const *iptr=(unsigned char *)buf; + unsigned long w; + switch(t) + { + case XUCS4: + for(; + (iptr<eptr)&&(w=*(unsigned long const *)iptr); + len++,iptr+=sizeof(unsigned long const)) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4BE: + for(;(w=UCS4BEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4LE: + for(;(w=UCS4LEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4_2143: + for(;(w=UCS4_2143toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUCS4_3412: + for(;(w=UCS4_3412toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16: + for(; + (w=xUTF16toUCS4((unsigned short const*&)iptr,eptr)); + len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16BE: + for(;(w=UTF16BEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF16LE: + for(;(w=UTF16LEtoUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XUTF8: + for(;(w=UTF8toUCS4(iptr,eptr));len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + case XEBCDIC: + for(;(iptr<eptr)&&(w=*iptr++);len++) + { + optr=UCS4toUTF8(w,optr); + } + break; + default: + break; + } + const unsigned int size=(size_t)optr-(size_t)utf8buf; + if(size) + { + retval=(gretval=GStringRep::Unicode::create(size)); + memcpy(retval->data,utf8buf,size); + }else + { + retval=(gretval=GStringRep::Unicode::create(1)); + retval->size=size; + } + retval->data[size]=0; + gutf8buf.resize(0); + const size_t s=(size_t)eptr-(size_t)iptr; + retval->set_remainder(iptr,s,t); + } + } + if(!retval) + { + retval=(gretval=GStringRep::Unicode::create(1)); + retval->data[0]=0; + retval->size=0; + retval->set_remainder(0,0,t); + } + return gretval; +} + +static unsigned long +xUTF16toUCS4(unsigned short const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned short const * const r=s+1; + if(r <= eptr) + { + unsigned long const W1=s[0]; + if((W1<0xD800)||(W1>0xDFFF)) + { + if((U=W1)) + { + s=r; + } + }else if(W1<=0xDBFF) + { + unsigned short const * const rr=r+1; + if(rr <= eptr) + { + unsigned long const W2=s[1]; + if(((W2>=0xDC00)||(W2<=0xDFFF))&&((U=(0x1000+((W1&0x3ff)<<10))|(W2&0x3ff)))) + { + s=rr; + }else + { + U=(unsigned int)(-1)-W1; + s=r; + } + } + } + } + return U; +} + +static unsigned long +UTF16BEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+2; + if(r <= eptr) + { + unsigned long const C1MSB=s[0]; + if((C1MSB<0xD8)||(C1MSB>0xDF)) + { + if((U=((C1MSB<<8)|((unsigned long)s[1])))) + { + s=r; + } + }else if(C1MSB<=0xDB) + { + unsigned char const * const rr=r+2; + if(rr <= eptr) + { + unsigned long const C2MSB=s[2]; + if((C2MSB>=0xDC)||(C2MSB<=0xDF)) + { + U=0x10000+((unsigned long)s[1]<<10)+(unsigned long)s[3] + +(((C1MSB<<18)|(C2MSB<<8))&0xc0300); + s=rr; + }else + { + U=(unsigned int)(-1)-((C1MSB<<8)|((unsigned long)s[1])); + s=r; + } + } + } + } + return U; +} + +static unsigned long +UTF16LEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+2; + if(r <= eptr) + { + unsigned long const C1MSB=s[1]; + if((C1MSB<0xD8)||(C1MSB>0xDF)) + { + if((U=((C1MSB<<8)|((unsigned long)s[0])))) + { + s=r; + } + }else if(C1MSB<=0xDB) + { + unsigned char const * const rr=r+2; + if(rr <= eptr) + { + unsigned long const C2MSB=s[3]; + if((C2MSB>=0xDC)||(C2MSB<=0xDF)) + { + U=0x10000+((unsigned long)s[0]<<10)+(unsigned long)s[2] + +(((C1MSB<<18)|(C2MSB<<8))&0xc0300); + s=rr; + }else + { + U=(unsigned int)(-1)-((C1MSB<<8)|((unsigned long)s[1])); + s=r; + } + } + } + } + return U; +} + +static unsigned long +UCS4BEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[0]<<8)|(unsigned long)s[1])<<8)|(unsigned long)s[2])<<8)|(unsigned long)s[3]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4LEtoUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[3]<<8)|(unsigned long)s[2])<<8)|(unsigned long)s[1])<<8)|(unsigned long)s[0]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4_2143toUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[1]<<8)|(unsigned long)s[0])<<8)|(unsigned long)s[3])<<8)|(unsigned long)s[2]); + if(U) + { + s=r; + } + } + return U; +} + +static unsigned long +UCS4_3412toUCS4(unsigned char const *&s,void const * const eptr) +{ + unsigned long U=0; + unsigned char const * const r=s+4; + if(r<=eptr) + { + U=(((((((unsigned long)s[2]<<8)|(unsigned long)s[3])<<8)|(unsigned long)s[0])<<8)|(unsigned long)s[1]); + if(U) + { + s=r; + } + } + return U; +} + +void +GStringRep::Unicode::set_remainder( void const * const buf, + const unsigned int size, const EncodeType xencodetype ) +{ + gremainder.resize(size,1); + if(size) + memcpy(remainder,buf,size); + encodetype=xencodetype; + encoding=0; +} + +void +GStringRep::Unicode::set_remainder( void const * const buf, + const unsigned int size, const GP<GStringRep> &xencoding ) +{ + gremainder.resize(size,1); + if(size) + memcpy(remainder,buf,size); + encoding=xencoding; + encodetype=XOTHER; +} + +void +GStringRep::Unicode::set_remainder( const GP<GStringRep::Unicode> &xremainder ) +{ + if(xremainder) + { + const int size=xremainder->gremainder; + gremainder.resize(size,1); + if(size) + memcpy(remainder,xremainder->remainder,size); + encodetype=xremainder->encodetype; + }else + { + gremainder.resize(0,1); + encodetype=XUTF8; + } +} + +GP<GStringRep::Unicode> +GStringRep::Unicode::get_remainder( void ) const +{ + return const_cast<GStringRep::Unicode *>(this); +} + +GUTF8String +GUTF8String::create(void const * const buf,const unsigned int size, + const EncodeType encodetype, const GUTF8String &encoding) +{ + return encoding.length() + ?create(buf,size,encodetype) + :create(buf,size,encoding); +} + +GUTF8String +GUTF8String::create( void const * const buf, + unsigned int size, const EncodeType encodetype ) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,encodetype)); + return retval; +} + +GUTF8String +GUTF8String::create( void const * const buf, + const unsigned int size, const GP<GStringRep::Unicode> &remainder) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,remainder)); + return retval; +} + +GUTF8String +GUTF8String::create( void const * const buf, + const unsigned int size, const GUTF8String &encoding ) +{ + GUTF8String retval; + retval.init(GStringRep::Unicode::create(buf,size,encoding )); + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp new file mode 100644 index 00000000..9bf184bd --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp @@ -0,0 +1,558 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IFFByteStream.cpp,v 1.10 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// -- Implementation of IFFByteStream +// - Author: Leon Bottou, 06/1998 + +// From: Leon Bottou, 1/31/2002 +// This has been changed by Lizardtech to fit better +// with their re-implementation of ByteStreams. + +#include <assert.h> +#include "IFFByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// Constructor +IFFByteStream::IFFByteStream(const GP<ByteStream> &xbs,const int xpos) +: ByteStream::Wrapper(xbs), has_magic(false), ctx(0), dir(0) +{ + offset = seekto = xpos; +} + +// Destructor +IFFByteStream::~IFFByteStream() +{ + while (ctx) + close_chunk(); +} + +GP<IFFByteStream> +IFFByteStream::create(const GP<ByteStream> &bs) +{ + const int pos=bs->tell(); + return new IFFByteStream(bs,pos); +} + + +// IFFByteStream::ready +// -- indicates if bytestream is ready for reading +// returns number of bytes available + +int +IFFByteStream::ready() +{ + if (ctx && dir < 0) + return ctx->offEnd - offset; + else if (ctx) + return 1; + else + return 0; +} + + +// IFFByteStream::composite +// -- indicates if bytestream is ready for putting or getting chunks + +int +IFFByteStream::composite() +{ + if (ctx && !ctx->bComposite) + return 0; + else + return 1; +} + + + + +// IFFByteStream::check_id +// -- checks if an id is legal + +int +IFFByteStream::check_id(const char *id) +{ + int i; + // check absence of null bytes + for (i=0; i<4; i++) + if (id[i]<0x20 || id[i]>0x7e) + return -1; + // check composite chunks + static char *szComposite[] = { "FORM", "LIST", "PROP", "CAT ", 0 }; + for (i=0; szComposite[i]; i++) + if (!memcmp(id, szComposite[i], 4)) + return 1; + // check reserved chunks + static char *szReserved[] = { "FOR", "LIS", "CAT", 0 }; + for (i=0; szReserved[i]; i++) + if (!memcmp(id, szReserved[i], 3) && id[3]>='1' && id[3]<='9') + return -1; + // regular chunk + return 0; +} + + + +// IFFByteStream::get_chunk +// -- get next chunk header + +int +IFFByteStream::get_chunk(GUTF8String &chkid, int *rawoffsetptr, int *rawsizeptr) +{ + int bytes; + char buffer[8]; + + // Check that we are allowed to read a chunk + if (dir > 0) + G_THROW( ERR_MSG("IFFByteStream.read_write") ); + if (ctx && !ctx->bComposite) + G_THROW( ERR_MSG("IFFByteStream.not_ready") ); + dir = -1; + + // Seek to end of previous chunk if necessary + if (seekto > offset) + { + bs->seek(seekto); + offset = seekto; + } + + // Skip padding byte + if (ctx && offset == ctx->offEnd) + return 0; + if (offset & 1) + { + bytes = bs->read( (void*)buffer, 1); + if (bytes==0 && !ctx) + return 0; + offset += bytes; + } + + // Record raw offset + int rawoffset = offset; + + // Read chunk id (skipping magic sequences inserted here to make + // DjVu files recognizable.) + for(;;) + { + if (ctx && offset == ctx->offEnd) + return 0; + if (ctx && offset+4 > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_end") ); + bytes = bs->readall( (void*)&buffer[0], 4); + offset = seekto = offset + bytes; + if (bytes==0 && !ctx) + return 0; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + if(buffer[0] != 0x41 || buffer[1] != 0x54 || + buffer[2] != 0x26 || buffer[3] != 0x54 ) + break; + has_magic=true; + } + + // Read chunk size + if (ctx && offset+4 > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_end2") ); + bytes = bs->readall( (void*)&buffer[4], 4); + offset = seekto = offset + bytes; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + long size = ((unsigned char)buffer[4]<<24) | + ((unsigned char)buffer[5]<<16) | + ((unsigned char)buffer[6]<<8) | + ((unsigned char)buffer[7]); + if (ctx && offset+size > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.corrupt_mangled") ); + + // Check if composite + int composite = check_id(buffer); + if (composite < 0) + G_THROW( ERR_MSG("IFFByteStream.corrupt_id") ); + + // Read secondary id of composite chunk + if (composite) + { + if (ctx && ctx->offEnd<offset+4) + G_THROW( ERR_MSG("IFFByteStream.corrupt_header") ); + bytes = bs->readall( (void*)&buffer[4], 4); + offset += bytes; + if (bytes != 4) + G_THROW( ByteStream::EndOfFile ); + if (check_id(&buffer[4])) + G_THROW( ERR_MSG("IFFByteStream.corrupt_2nd_id") ); + } + + // Create context record + IFFContext *nctx = new IFFContext; + G_TRY + { + nctx->next = ctx; + nctx->offStart = seekto; + nctx->offEnd = seekto + size; + if (composite) + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4); + nctx->bComposite = 1; + } + else + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memset( (void*)(nctx->idTwo), 0, 4); + nctx->bComposite = 0; + } + } + G_CATCH_ALL + { + delete nctx; + G_RETHROW; + } + G_ENDCATCH; + + // Install context record + ctx = nctx; + chkid = GUTF8String(ctx->idOne, 4); + if (composite) + chkid = chkid + ":" + GUTF8String(ctx->idTwo, 4); + + // Return + if (rawoffsetptr) + *rawoffsetptr = rawoffset; + if (rawsizeptr) + *rawsizeptr = ( ctx->offEnd - rawoffset + 1) & ~0x1; + return size; +} + + + +// IFFByteStream::put_chunk +// -- write new chunk header + +void +IFFByteStream::put_chunk(const char *chkid, int insert_magic) +{ + int bytes; + char buffer[8]; + + // Check that we are allowed to write a chunk + if (dir < 0) + G_THROW( ERR_MSG("IFFByteStream.read_write") ); + if (ctx && !ctx->bComposite) + G_THROW( ERR_MSG("IFFByteStream.not_ready2") ); + dir = +1; + + // Check primary id + int composite = check_id(chkid); + if ((composite<0) || (composite==0 && chkid[4]) + || (composite && (chkid[4]!=':' || check_id(&chkid[5]) || chkid[9])) ) + G_THROW( ERR_MSG("IFFByteStream.bad_chunk") ); + + // Write padding byte + assert(seekto <= offset); + memset((void*)buffer, 0, 8); + if (offset & 1) + offset += bs->write((void*)&buffer[4], 1); + + // Insert magic to make this file recognizable as DjVu + if (insert_magic) + { + // Don't change the way you do the file magic! + // I rely on these bytes letters in some places + // (like DjVmFile.cpp and djvm.cpp) -- eaf + buffer[0]=0x41; + buffer[1]=0x54; + buffer[2]=0x26; + buffer[3]=0x54; + offset += bs->writall((void*)&buffer[0], 4); + } + + // Write chunk header + memcpy((void*)&buffer[0], (void*)&chkid[0], 4); + bytes = bs->writall((void*)&buffer[0], 8); + offset = seekto = offset + bytes; + if (composite) + { + memcpy((void*)&buffer[4], (void*)&chkid[5], 4); + bytes = bs->writall((void*)&buffer[4], 4); + offset = offset + bytes; + } + + // Create new context record + IFFContext *nctx = new IFFContext; + G_TRY + { + nctx->next = ctx; + nctx->offStart = seekto; + nctx->offEnd = 0; + if (composite) + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4); + nctx->bComposite = 1; + } + else + { + memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4); + memset( (void*)(nctx->idTwo), 0, 4); + nctx->bComposite = 0; + } + } + G_CATCH_ALL + { + delete nctx; + G_RETHROW; + } + G_ENDCATCH; + // Install context record and leave + ctx = nctx; +} + + + +void +IFFByteStream::close_chunk() +{ + // Check that this is ok + if (!ctx) + G_THROW( ERR_MSG("IFFByteStream.cant_close") ); + // Patch size field in new chunk + if (dir > 0) + { + ctx->offEnd = offset; + long size = ctx->offEnd - ctx->offStart; + char buffer[4]; + buffer[0] = (unsigned char)(size>>24); + buffer[1] = (unsigned char)(size>>16); + buffer[2] = (unsigned char)(size>>8); + buffer[3] = (unsigned char)(size); + bs->seek(ctx->offStart - 4); + bs->writall((void*)buffer, 4); + bs->seek(offset); + } + // Arrange for reader to seek at next chunk + seekto = ctx->offEnd; + // Remove ctx record + IFFContext *octx = ctx; + ctx = octx->next; + assert(ctx==0 || ctx->bComposite); + delete octx; +} + +// This is the same as above, but adds a seek to the close +// Otherwise an EOF in this chunk won't be reported until we +// try to open the next chunk, which makes error recovery +// very difficult. +void +IFFByteStream::seek_close_chunk(void) +{ + close_chunk(); + if ((dir <= 0)&&((!ctx)||(ctx->bComposite))&&(seekto > offset)) + { + bs->seek(seekto); + offset = seekto; + } +} + +// IFFByteStream::short_id +// Returns the id of the current chunk + +void +IFFByteStream::short_id(GUTF8String &chkid) +{ + if (!ctx) + G_THROW( ERR_MSG("IFFByteStream.no_chunk_id") ); + if (ctx->bComposite) + chkid = GUTF8String(ctx->idOne, 4) + ":" + GUTF8String(ctx->idTwo, 4); + else + chkid = GUTF8String(ctx->idOne, 4); +} + + +// IFFByteStream::full_id +// Returns the full chunk id of the current chunk + +void +IFFByteStream::full_id(GUTF8String &chkid) +{ + short_id(chkid); + if (ctx->bComposite) + return; + // Search parent FORM or PROP chunk. + for (IFFContext *ct = ctx->next; ct; ct=ct->next) + if (memcmp(ct->idOne, "FOR", 3)==0 || + memcmp(ct->idOne, "PRO", 3)==0 ) + { + chkid = GUTF8String(ct->idTwo, 4) + "." + chkid; + break; + } +} + + + +// IFFByteStream::read +// -- read bytes from IFF file chunk + +size_t +IFFByteStream::read(void *buffer, size_t size) +{ + if (! (ctx && dir < 0)) + G_THROW( ERR_MSG("IFFByteStream.not_ready3") ); + // Seek if necessary + if (seekto > offset) { + bs->seek(seekto); + offset = seekto; + } + // Ensure that read does not extend beyond chunk + if (offset > ctx->offEnd) + G_THROW( ERR_MSG("IFFByteStream.bad_offset") ); + if (offset + (long)size > ctx->offEnd) + size = (size_t) (ctx->offEnd - offset); + // Read bytes + size_t bytes = bs->read(buffer, size); + offset += bytes; + return bytes; +} + + +// IFFByteStream::write +// -- write bytes to IFF file chunk + +size_t +IFFByteStream::write(const void *buffer, size_t size) +{ + if (! (ctx && dir > 0)) + G_THROW( ERR_MSG("IFFByteStream.not_ready4") ); + if (seekto > offset) + G_THROW( ERR_MSG("IFFByteStream.cant_write") ); + size_t bytes = bs->write(buffer, size); + offset += bytes; + return bytes; +} + +// IFFByteStream::tell +// -- tell position + +long +IFFByteStream::tell() const +{ + return (seekto>offset)?seekto:offset; +} + +bool +IFFByteStream::compare(IFFByteStream &iff) +{ + bool retval=(&iff == this); + if(!retval) + { + GUTF8String chkid1, chkid2; + int size; + while((size=get_chunk(chkid1)) == iff.get_chunk(chkid2)) + { + if(chkid1 != chkid2) + { + break; + } + if(!size) + { + retval=true; + break; + } + char buf[4096]; + int len; + while((len=read(buf,sizeof(buf)))) + { + int s=0; + char buf2[sizeof(buf)]; + while(s<len) + { + const int i=iff.read(buf2+s,len-s); + if(!i) + break; + s+=i; + } + if((s != len)||memcmp(buf,buf2,len)) + break; + } + if(len) + break; + iff.close_chunk(); + close_chunk(); + } + } + return retval; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h new file mode 100644 index 00000000..cb1fb616 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IFFByteStream.h @@ -0,0 +1,312 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IFFByteStream.h,v 1.10 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _IFFBYTESTREAM_H_ +#define _IFFBYTESTREAM_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name IFFByteStream.h + + Files #"IFFByteStream.h"# and #"IFFByteStream.cpp"# implement a parser for + files structured according the Electronic Arts ``EA IFF 85 Interchange + File Format''. IFF files are composed of a sequence of data {\em chunks}. + Each chunk is identified by a four character {\em chunk identifier} + describing the type of the data stored in the chunk. A few special chunk + identifiers, for instance #"FORM"#, are reserved for {\em composite + chunks} which themselves contain a sequence of data chunks. This + conventions effectively provides IFF files with a convenient hierarchical + structure. Composite chunks are further identified by a secondary chunk + identifier. + + We found convenient to define a {\em extended chunk identifier}. In the + case of a regular chunk, the extended chunk identifier is simply the + chunk identifier, as in #"PM44"#. In the case of a composite chunk, the + extended chunk identifier is composed by concatenating the main chunk + identifier, a colon, and the secondary chunk identifier, as in + #"FORM:DJVU"#. + + Class \Ref{IFFByteStream} provides a way to read or write IFF structured + files. Member functions provide an easy mean to position the underlying + \Ref{ByteStream} at the beginning of each chunk and to read or write the + data until reaching the end of the chunk. The utility program + \Ref{djvuinfo} demonstrates how to use class #IFFByteStream#. + + {\bf IFF Files and ZP-Coder} --- + Class #IFFByteStream# repositions the underlying ByteStream whenever a new + chunk is accessed. It is possible to code chunk data with the ZP-Coder + without worrying about the final file position. See class \Ref{ZPCodec} + for more details. + + {\bf DjVu IFF Files} --- We had initially planned to exactly follow the + IFF specifications. Then we realized that certain versions of MSIE + recognize any IFF file as a Microsoft AIFF sound file and pop a message + box "Cannot play that sound". It appears that the structure of AIFF files + is entirely modeled after the IFF standard, with small variations + regarding the endianness of numbers and the padding rules. We eliminate + this problem by casting the octet protection spell. Our IFF files always + start with the four octets #0x41,0x54,0x26,0x54# followed by the fully + conformant IFF byte stream. Class #IFFByteStream# silently skips these + four octets when it encounters them. + + {\bf References} --- EA IFF 85 Interchange File Format specification:\\ + \URL{http://www.cica.indiana.edu/graphics/image_specs/ilbm.format.txt} or + \URL{http://www.tnt.uni-hannover.de/soft/compgraph/fileformats/docs/iff.pre} + + @memo + IFF file parser. + @author + L\'eon Bottou <[email protected]> + +// From: Leon Bottou, 1/31/2002 +// This has been changed by Lizardtech to fit better +// with their re-implementation of ByteStreams. + + @version + #$Id: IFFByteStream.h,v 1.10 2003/11/07 22:08:21 leonb Exp $# */ +//@{ + + +#include "DjVuGlobal.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "GException.h" +#include "GString.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** ByteStream interface for an IFF file. + + Class #IFFByteStream# augments the #ByteStream# interface with + functions for navigating from chunk to chunk. It works in relation + with a ByteStream specified at construction time. + + {\bf Reading an IFF file} --- You can read an IFF file by constructing an + #IFFByteStream# object attached to the ByteStream containing the IFF file. + Calling function \Ref{get_chunk} positions the file pointer at the + beginning of the first chunk. You can then use \Ref{ByteStream::read} to + access the chunk data. Function #read# will return #0# if you attempt to + read past the end of the chunk, just as if you were trying to read past + the end of a file. You can at any time call function \Ref{close_chunk} to + terminate reading data in this chunk. The following chunks can be + accessed by calling #get_chunk# and #close_chunk# repeatedly until you + reach the end of the file. Function #read# is not very useful when + accessing a composite chunk. You can instead make nested calls to + functions #get_chunk# and #close_chunk# in order to access the chunks + located inside the composite chunk. + + {\bf Writing an IFF file} --- You can write an IFF file by constructing an + #IFFByteStream# object attached to the seekable ByteStream object that + will contain the IFF file. Calling function \Ref{put_chunk} creates a + first chunk header and positions the file pointer at the beginning of the + chunk. You can then use \Ref{ByteStream::write} to store the chunk data. + Calling function \Ref{close_chunk} terminates the current chunk. You can + append more chunks by calling #put_chunk# and #close_chunk# repeatedly. + Function #write# is not very useful for writing a composite chunk. You + can instead make nested calls to function #put_chunk# and #close_chunk# in + order to create chunks located inside the composite chunk. + + Writing an IFF file requires a seekable ByteStream (see + \Ref{ByteStream::is_seekable}). This is not much of a problem because you + can always create the IFF file into a \Ref{MemoryByteStream} and then use + \Ref{ByteStream::copy} to transfer the IFF file into a non seekable + ByteStream. */ + +class IFFByteStream : protected ByteStream::Wrapper +{ +protected: + IFFByteStream(const GP<ByteStream> &bs, const int pos); +public: + /** Constructs an IFFByteStream object attached to ByteStream #bs#. + Any ByteStream can be used when reading an IFF file. Writing + an IFF file however requires a seekable ByteStream. */ + static GP<IFFByteStream> create(const GP<ByteStream> &bs); + // --- BYTESTREAM INTERFACE + ~IFFByteStream(); + virtual size_t read(void *buffer, size_t size); + virtual size_t write(const void *buffer, size_t size); + virtual long tell(void) const; + // -- NAVIGATING CHUNKS + /** Enters a chunk for reading. Function #get_chunk# returns zero when the + last chunk has already been accessed. Otherwise it parses a chunk + header, positions the IFFByteStream at the beginning of the chunk data, + stores the extended chunk identifier into string #chkid#, and returns + the non zero chunk size. The file offset of the chunk data may be + retrieved using function #tell#. The chunk data can then be read using + function #read# until reaching the end of the chunk. Advanced users may + supply two pointers to integer variables using arguments #rawoffsetptr# + and #rawsizeptr#. These variables will be overwritten with the offset + and the length of the file segment containing both the chunk header and + the chunk data. */ + int get_chunk(GUTF8String &chkid, int *rawoffsetptr=0, int *rawsizeptr=0); + /** Enters a chunk for writing. Function #put_chunk# prepares a chunk + header and positions the IFFByteStream at the beginning of the chunk + data. Argument #chkid# defines a extended chunk identifier for this + chunk. The chunk data can then be written using function #write#. The + chunk is terminated by a matching call to function #close_chunk#. When + #insertmagic# is non zero, function #put_chunk# inserts the bytes: + 0x41, 0x54, 0x26, 0x54 before the chunk header, as discussed in + \Ref{IFFByteStream.h}. */ + void put_chunk(const char *chkid, int insertmagic=0); + /** Leaves the current chunk. This function leaves the chunk previously + entered by a matching call to #get_chunk# and #put_chunk#. The + IFFByteStream is then ready to process the next chunk at the same + hierarchical level. */ + void close_chunk(); + /** This is identical to the above, plus it adds a seek to the start of + the next chunk. This way we catch EOF errors with the current chunk.*/ + void seek_close_chunk(); + /** Returns true when it is legal to call #read# or #write#. */ + int ready(); + /** Returns true when the current chunk is a composite chunk. */ + int composite(); + /** Returns the current chunk identifier of the current chunk. String + #chkid# is overwritten with the {\em extended chunk identifier} of the + current chunk. The extended chunk identifier of a regular chunk is + simply the chunk identifier, as in #"PM44"#. The extended chunk + identifier of a composite chunk is the concatenation of the chunk + identifier, of a semicolon #":"#, and of the secondary chunk identifier, + as in #"FORM:DJVU"#. */ + void short_id(GUTF8String &chkid); + /** Returns the qualified chunk identifier of the current chunk. String + #chkid# is overwritten with the {\em qualified chunk identifier} of the + current chunk. The qualified chunk identifier of a composite chunk is + equal to the extended chunk identifier. The qualified chunk identifier + of a regular chunk is composed by concatenating the secondary chunk + identifier of the closest #"FORM"# or #"PROP"# composite chunk + containing the current chunk, a dot #"."#, and the current chunk + identifier, as in #"DJVU.INFO"#. According to the EA IFF 85 identifier + scoping rules, the qualified chunk identifier uniquely defines how the + chunk data should be interpreted. */ + void full_id(GUTF8String &chkid); + /** Checks a potential chunk identifier. This function categorizes the + chunk identifier formed by the first four characters of string #chkid#. + It returns #0# if this is a legal identifier for a regular chunk. It + returns #+1# if this is a reserved composite chunk identifier. It + returns #-1# if this is an illegal or otherwise reserved identifier + which should not be used. */ + static int check_id(const char *id); + GP<ByteStream> get_bytestream(void) {return this;} + /** Copy data from another ByteStream. A maximum of #size# bytes are read + from the ByteStream #bsfrom# and are written to the ByteStream #*this# + at the current position. Less than #size# bytes may be written if an + end-of-file mark is reached on #bsfrom#. This function returns the + total number of bytes copied. Setting argument #size# to zero (the + default value) has a special meaning: the copying process will continue + until reaching the end-of-file mark on ByteStream #bsfrom#, regardless + of the number of bytes transferred. */ + size_t copy(ByteStream &bsfrom, size_t size=0) + { return get_bytestream()->copy(bsfrom,size); } + /** Flushes all buffers in the ByteStream. Calling this function + guarantees that pending data have been actually written (i.e. passed to + the operating system). Class #ByteStream# provides a default + implementation which does nothing. */ + virtual void flush(void) + { ByteStream::Wrapper::flush(); } + /** This is a simple compare method. The IFFByteStream may be read for + the sake of the comparison. Since IFFByteStreams are non-seekable, + the stream is not valid for use after comparing, regardless of the + result. */ + bool compare(IFFByteStream &iff); + /** #has_magic# is true if the stream has the DjVu file magic. + */ + bool has_magic; +private: + // private datatype + struct IFFContext + { + IFFContext *next; + long offStart; + long offEnd; + char idOne[4]; + char idTwo[4]; + char bComposite; + }; + // Implementation + IFFContext *ctx; + long offset; + long seekto; + int dir; + // Cancel C++ default stuff + IFFByteStream(const IFFByteStream &); + IFFByteStream & operator=(const IFFByteStream &); + static GP<IFFByteStream> create(ByteStream *bs); +}; + +//@} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp new file mode 100644 index 00000000..c63eda7d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp @@ -0,0 +1,1797 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44EncodeCodec.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 08/1998 +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split this file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#define IW44IMAGE_IMPLIMENTATION /* */ + + + +#include "IW44Image.h" +#include "ZPCodec.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "IFFByteStream.h" +#include "GRect.h" + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "MMX.h" +#undef IWTRANSFORM_TIMER +#ifdef IWTRANSFORM_TIMER +#include "GOS.h" +#endif + +#include <assert.h> +#include <string.h> +#include <math.h> + +#ifndef NEED_DECODER_ONLY + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +#define IWALLOCSIZE 4080 +#define IWCODEC_MAJOR 1 +#define IWCODEC_MINOR 2 +#define DECIBEL_PRUNE 5.0 + + +////////////////////////////////////////////////////// +// WAVELET DECOMPOSITION CONSTANTS +////////////////////////////////////////////////////// + +// Parameters for IW44 wavelet. +// - iw_norm: norm of all wavelets (for db estimation) +// - iw_shift: scale applied before decomposition + + +static const float iw_norm[16] = { + 2.627989e+03F, + 1.832893e+02F, 1.832959e+02F, 5.114690e+01F, + 4.583344e+01F, 4.583462e+01F, 1.279225e+01F, + 1.149671e+01F, 1.149712e+01F, 3.218888e+00F, + 2.999281e+00F, 2.999476e+00F, 8.733161e-01F, + 1.074451e+00F, 1.074511e+00F, 4.289318e-01F +}; + +static const int iw_shift = 6; +static const int iw_round = (1<<(iw_shift-1)); + +static const struct { int start; int size; } +bandbuckets[] = +{ + // Code first bucket and number of buckets in each band + { 0, 1 }, // -- band zero contains all lores info + { 1, 1 }, { 2, 1 }, { 3, 1 }, + { 4, 4 }, { 8, 4 }, { 12,4 }, + { 16,16 }, { 32,16 }, { 48,16 }, +}; + + +/** IW44 encoded gray-level image. This class provided functions for managing + a gray level image represented as a collection of IW44 wavelet + coefficients. The coefficients are stored in a memory efficient data + structure. Member function \Ref{get_bitmap} renders an arbitrary segment + of the image into a \Ref{GBitmap}. Member functions \Ref{decode_iff} and + \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). + Both the copy constructor and the copy operator are declared as private + members. It is therefore not possible to make multiple copies of instances + of this class. */ + +class IWBitmap::Encode : public IWBitmap +{ +public: + /// Destructor + virtual ~Encode(void); + /** Null constructor. Constructs an empty IWBitmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + Encode(void); + /** Initializes an IWBitmap with image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). */ + void init(const GBitmap &bm, const GP<GBitmap> mask=0); + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a gray level image into DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:BM44#) composed of #nchunks# chunks + (identifier #BM44#). Data for each chunk is generated with + #encode_chunk# using the corresponding parameters in array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); +protected: + Codec::Encode *ycodec_enc; +}; + +/** IW44 encoded color image. This class provided functions for managing a + color image represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_pixmap} renders an arbitrary segment of the image into a + \Ref{GPixmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IWPixmap::Encode : public IWPixmap +{ +public: + enum CRCBMode { + CRCBnone=IW44Image::CRCBnone, + CRCBhalf=IW44Image::CRCBhalf, + CRCBnormal=IW44Image::CRCBnormal, + CRCBfull=IW44Image::CRCBfull }; + /// Destructor + virtual ~Encode(void); + /** Null constructor. Constructs an empty IWPixmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + Encode(void); + /** Initializes an IWPixmap with color image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). + Argument #crcbmode# specifies how the chrominance information should be + encoded (see \Ref{CRCBMode}). */ + void init(const GPixmap &bm, const GP<GBitmap> mask=0, CRCBMode crcbmode=CRCBnormal); + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a color image into a DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:PM44#) composed of #nchunks# chunks + (identifier #PM44#). Data for each chunk is generated with + #encode_chunk# using the corresponding parameters in array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); +protected: + Codec::Encode *ycodec_enc, *cbcodec_enc, *crcodec_enc; +}; + +class IW44Image::Map::Encode : public IW44Image::Map // DJVU_CLASS +{ +public: + Encode(const int w, const int h) : Map(w,h) {} + // creation (from image) + void create(const signed char *img8, int imgrowsize, + const signed char *msk8=0, int mskrowsize=0); + // slash resolution + void slashres(int res); +}; + +class IW44Image::Codec::Encode : public IW44Image::Codec +{ +public: + Encode(IW44Image::Map &map); + // Coding + virtual int code_slice(ZPCodec &zp); + float estimate_decibel(float frac); + // Data + void encode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, IW44Image::Block &eblk, int fbucket, int nbucket); + int encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk); + IW44Image::Map emap; +}; + +IW44Image::Codec::Encode::Encode(IW44Image::Map &map) +: Codec(map), emap(map.iw,map.ih) {} + +////////////////////////////////////////////////////// +/** IW44Image::Transform::Encode +*/ + +class IW44Image::Transform::Encode : IW44Image::Transform +{ + public: + // WAVELET TRANSFORM + /** Forward transform. */ + static void forward(short *p, int w, int h, int rowsize, int begin, int end); + + // COLOR TRANSFORM + /** Extracts Y */ + static void RGB_to_Y(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); + /** Extracts Cb */ + static void RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); + /** Extracts Cr */ + static void RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize); +}; + + +////////////////////////////////////////////////////// +// MMX IMPLEMENTATION HELPERS +////////////////////////////////////////////////////// + + +// Note: +// MMX implementation for vertical transforms only. +// Speedup is basically related to faster memory transfer +// The IW44 transform is not CPU bound, it is memory bound. + +#ifdef MMX + +static const short w9[] = {9,9,9,9}; +static const short w1[] = {1,1,1,1}; +static const int d8[] = {8,8}; +static const int d16[] = {16,16}; + + +static inline void +mmx_fv_1 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d8,mm0); + MMXar( paddd, d8,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 4,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 4,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( psubw, mm0,mm7); // MM7=[ p3-x3, p2-x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +static inline void +mmx_fv_2 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q ++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d16,mm0); + MMXar( paddd, d16,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 5,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 5,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( paddw, mm0,mm7); // MM7=[ p3+x3, p2+x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +#endif /* MMX */ + +////////////////////////////////////////////////////// +// NEW FILTERS +////////////////////////////////////////////////////// + +static void +filter_fv(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale*rowsize; + int s3 = s+s+s; + h = ((h-1)/scale)+1; + y += 1; + p += s; + while (y-3 < h) + { + // 1-Delta + { + short *q = p; + short *e = q+w; + if (y>=3 && y+3<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_1(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q += scale; + } + } + else if (y<h) + { + // Special cases + short *q1 = (y+1<h ? q+s : q-s); + while (q<e) + { + int a = (int)q[-s] + (int)(*q1); + *q -= ((a+1)>>1); + q += scale; + q1 += scale; + } + } + } + // 2-Update + { + short *q = p-s3; + short *e = q+w; + if (y>=6 && y<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_2(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q += scale; + } + } + else if (y>=3) + { + // Special cases + short *q1 = (y-2<h ? q+s : 0); + short *q3 = (y<h ? q+s3 : 0); + if (y>=6) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (int)q[-s3] + (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else if (y>=4) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else + { + while (q<e) + { + int a = (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + } + } + y += 2; + p += s+s; + } +} + +static void +filter_fh(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale; + int s3 = s+s+s; + rowsize *= scale; + while (y<h) + { + short *q = p+s; + short *e = p+w; + int a0=0, a1=0, a2=0, a3=0; + int b0=0, b1=0, b2=0, b3=0; + if (q < e) + { + // Special case: x=1 + a1 = a2 = a3 = q[-s]; + if (q+s<e) + a2 = q[s]; + if (q+s3<e) + a3 = q[s3]; + b3 = q[0] - ((a1+a2+1)>>1); + q[0] = b3; + q += s+s; + } + while (q+s3 < e) + { + // Generic case + a0=a1; + a1=a2; + a2=a3; + a3=q[s3]; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+8) >> 4); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + while (q < e) + { + // Special case: w-3 <= x < w + a1=a2; + a2=a3; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((a1+a2+1)>>1); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + while (q-s3 < e) + { + // Special case w <= x < w+3 + b0=b1; + b1=b2; + b2=b3; + b3=0; + if (q-s3 >= p) + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); + q += s+s; + } + y += scale; + p += rowsize; + } +} + + +////////////////////////////////////////////////////// +// WAVELET TRANSFORM +////////////////////////////////////////////////////// + + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) + +void +IW44Image::Transform::Encode::forward(short *p, int w, int h, int rowsize, int begin, int end) +{ + + // PREPARATION + filter_begin(w,h); + // LOOP ON SCALES + for (int scale=begin; scale<end; scale<<=1) + { +#ifdef IWTRANSFORM_TIMER + int tv,th; + th = tv = GOS::ticks(); +#endif + filter_fh(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks(); + tv = th - tv; +#endif + filter_fv(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks()-th; + DjVuPrintErrorUTF8("forw%d\tv=%dms h=%dms\n", scale,th,tv); +#endif + } + // TERMINATE + filter_end(); +} + +////////////////////////////////////////////////////// +// COLOR TRANSFORM +////////////////////////////////////////////////////// + +static const float +rgb_to_ycc[3][3] = +{ { 0.304348F, 0.608696F, 0.086956F }, + { 0.463768F, -0.405797F, -0.057971F }, + {-0.173913F, -0.347826F, 0.521739F } }; + + +/* Extracts Y */ +void +IW44Image::Transform::Encode::RGB_to_Y(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)(k*0x10000*rgb_to_ycc[0][0]); + gmul[k] = (int)(k*0x10000*rgb_to_ycc[0][1]); + bmul[k] = (int)(k*0x10000*rgb_to_ycc[0][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int y = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = (y>>16) - 128; + } + } +} + +#ifdef min +#undef min +#endif +static inline int min(const int x,const int y) {return (x<y)?x:y;} +#ifdef max +#undef max +#endif +static inline int max(const int x,const int y) {return (x>y)?x:y;} + +/* Extracts Cb */ +void +IW44Image::Transform::Encode::RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)(k*0x10000*rgb_to_ycc[2][0]); + gmul[k] = (int)(k*0x10000*rgb_to_ycc[2][1]); + bmul[k] = (int)(k*0x10000*rgb_to_ycc[2][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = max(-128, min(127, c>>16)); + } + } +} + +/* Extracts Cr */ +void +IW44Image::Transform::Encode::RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, + signed char *out, int outrowsize) +{ + int rmul[256], gmul[256], bmul[256]; + for (int k=0; k<256; k++) + { + rmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][0]); + gmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][1]); + bmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + gmul[p2->g] + bmul[p2->b] + 32768; + *out2 = max(-128, min(127, c>>16)); + } + } +} + + +////////////////////////////////////////////////////// +// MASKING DECOMPOSITION +////////////////////////////////////////////////////// + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) +// with a MASK bitmap + + +static void +interpolate_mask(short *data16, int w, int h, int rowsize, + const signed char *mask8, int mskrowsize) +{ + int i,j; + // count masked bits + short *count; + GPBuffer<short> gcount(count,w*h); + short *cp = count; + for (i=0; i<h; i++, cp+=w, mask8+=mskrowsize) + for (j=0; j<w; j++) + cp[j] = (mask8[j] ? 0 : 0x1000); + // copy image + short *sdata; + GPBuffer<short> gsdata(sdata,w*h); + short *p = sdata; + short *q = data16; + for (i=0; i<h; i++, p+=w, q+=rowsize) + for (j=0; j<w; j++) + p[j] = q[j]; + // iterate over resolutions + int split = 1; + int scale = 2; + int again = 1; + while (again && scale<w && scale<h) + { + again = 0; + p = data16; + q = sdata; + cp = count; + // iterate over block + for (i=0; i<h; i+=scale, cp+=w*scale, q+=w*scale, p+=rowsize*scale) + for (j=0; j<w; j+=scale) + { + int ii, jj; + int gotz = 0; + int gray = 0; + int npix = 0; + short *cpp = cp; + short *qq = q; + // look around when square goes beyond border + int istart = i; + if (istart+split>h) + { + istart -= scale; + cpp -= w*scale; + qq -= w*scale; + } + int jstart = j; + if (jstart+split>w) + jstart -= scale; + // compute gray level + for (ii=istart; ii<i+scale && ii<h; ii+=split, cpp+=w*split, qq+=w*split) + for (jj=jstart; jj<j+scale && jj<w; jj+=split) + { + if (cpp[jj]>0) + { + npix += cpp[jj]; + gray += cpp[jj] * qq[jj]; + } + else if (ii>=i && jj>=j) + { + gotz = 1; + } + } + // process result + if (npix == 0) + { + // continue to next resolution + again = 1; + cp[j] = 0; + } + else + { + gray = gray / npix; + // check whether initial image require fix + if (gotz) + { + cpp = cp; + qq = p; + for (ii=i; ii<i+scale && ii<h; ii+=1, cpp+=w, qq+=rowsize) + for (jj=j; jj<j+scale && jj<w; jj+=1) + if (cpp[jj] == 0) + { + qq[jj] = gray; + cpp[jj] = 1; + } + } + // store average for next iteration + cp[j] = npix>>2; + q[j] = gray; + } + } + // double resolution + split = scale; + scale = scale+scale; + } +} + + +static void +forward_mask(short *data16, int w, int h, int rowsize, int begin, int end, + const signed char *mask8, int mskrowsize ) +{ + int i,j; + signed char *m; + short *p; + short *d; + // Allocate buffers + short *sdata; + GPBuffer<short> gsdata(sdata,w*h); + signed char *smask; + GPBuffer<signed char> gsmask(smask,w*h); + // Copy mask + m = smask; + for (i=0; i<h; i+=1, m+=w, mask8+=mskrowsize) + memcpy((void*)m, (void*)mask8, w); + // Loop over scale + for (int scale=begin; scale<end; scale<<=1) + { + // Copy data into sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + d[j] = p[j]; + p += rowsize * scale; + d += w * scale; + } + // Decompose + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Cancel masked coefficients + d = sdata; + m = smask; + for (i=0; i<h; i+=scale+scale) + { + for (j=scale; j<w; j+=scale+scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + if (i+scale < h) + { + for (j=0; j<w; j+=scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + } + } + // Reconstruct + IW44Image::Transform::Decode::backward(sdata, w, h, w, scale+scale, scale); + // Correct visible pixels + p = data16; + d = sdata; + m = smask; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + if (! m[j]) + d[j] = p[j]; + p += rowsize*scale; + m += w*scale; + d += w*scale; + } + // Decompose again (no need to iterate actually!) + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Copy coefficients from sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + p[j] = d[j]; + p += rowsize * scale; + d += w * scale; + } + // Compute new mask for next scale + m = smask; + signed char *m0 = m; + signed char *m1 = m; + for (i=0; i<h; i+=scale+scale) + { + m0 = m1; + if (i+scale < h) + m1 = m + w*scale; + for (j=0; j<w; j+=scale+scale) + if (m[j] && m0[j] && m1[j] && (j<=0 || m[j-scale]) && (j+scale>=w || m[j+scale])) + m[j] = 1; + else + m[j] = 0; + m = m1 + w*scale; + } + } + // Free buffers +} + +void +IW44Image::Map::Encode::create(const signed char *img8, int imgrowsize, + const signed char *msk8, int mskrowsize ) +{ + int i, j; + // Progress + DJVU_PROGRESS_TASK(transf,"create iw44 map",3); + // Allocate decomposition buffer + short *data16; + GPBuffer<short> gdata16(data16,bw*bh); + // Copy pixels + short *p = data16; + const signed char *row = img8; + for (i=0; i<ih; i++) + { + for (j=0; j<iw; j++) + *p++ = (int)(row[j]) << iw_shift; + row += imgrowsize; + for (j=iw; j<bw; j++) + *p++ = 0; + } + for (i=ih; i<bh; i++) + for (j=0; j<bw; j++) + *p++ = 0; + // Handle bitmask + if (msk8) + { + // Interpolate pixels below mask + DJVU_PROGRESS_RUN(transf, 1); + interpolate_mask(data16, iw, ih, bw, msk8, mskrowsize); + // Multiscale iterative masked decomposition + DJVU_PROGRESS_RUN(transf, 3); + forward_mask(data16, iw, ih, bw, 1, 32, msk8, mskrowsize); + } + else + { + // Perform traditional decomposition + DJVU_PROGRESS_RUN(transf, 3); + IW44Image::Transform::Encode::forward(data16, iw, ih, bw, 1, 32); + } + // Copy coefficient into blocks + p = data16; + IW44Image::Block *block = blocks; + for (i=0; i<bh; i+=32) + { + for (j=0; j<bw; j+=32) + { + short liftblock[1024]; + // transfer coefficients at (p+j) into aligned block + short *pp = p + j; + short *pl = liftblock; + for (int ii=0; ii<32; ii++, pp+=bw) + for (int jj=0; jj<32; jj++) + *pl++ = pp[jj]; + // transfer into IW44Image::Block (apply zigzag and scaling) + block->read_liftblock(liftblock, this); + block++; + } + // next row of blocks + p += 32*bw; + } +} + +void +IW44Image::Map::Encode::slashres(int res) +{ + int minbucket = 1; + if (res < 2) + return; + else if (res < 4) + minbucket=16; + else if (res < 8) + minbucket=4; + for (int blockno=0; blockno<nb; blockno++) + for (int buckno=minbucket; buckno<64; buckno++) + blocks[blockno].zero(buckno); +} + +// encode_prepare +// -- compute the states prior to encoding the buckets +int +IW44Image::Codec::Encode::encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk) +{ + int bbstate = 0; + // compute state of all coefficients in all buckets + if (band) + { + // Band other than zero + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + int bstatetmp = 0; + if (! pcoeff) + { + bstatetmp = UNK; + // cstate[i] is not used and does not need initialization + } + else if (! epcoeff) + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if (epcoeff[i]) + cstatetmp = ACTIVE; + else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + bucketstate[buckno] = bstatetmp; + bbstate |= bstatetmp; + } + } + else + { + // Band zero ( fbucket==0 implies band==zero and nbucket==1 ) + const short *pcoeff = blk.data(0, &map); + const short *epcoeff = eblk.data(0, &emap); + char *cstate = coeffstate; + for (int i=0; i<16; i++) + { + int thres = quant_lo[i]; + int cstatetmp = cstate[i]; + if (cstatetmp != ZERO) + { + cstatetmp = UNK; + if (epcoeff[i]) + cstatetmp = ACTIVE; + else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) + cstatetmp = NEW|UNK; + } + cstate[i] = cstatetmp; + bbstate |= cstatetmp; + } + bucketstate[0] = bbstate; + } + return bbstate; +} + +// encode_buckets +// -- code a sequence of buckets in a given block +void +IW44Image::Codec::Encode::encode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, IW44Image::Block &eblk, + int fbucket, int nbucket) +{ + // compute state of all coefficients in all buckets + int bbstate = encode_prepare(band, fbucket, nbucket, blk, eblk); + + // code root bit + if ((nbucket<16) || (bbstate&ACTIVE)) + { + bbstate |= NEW; + } + else if (bbstate & UNK) + { + zp.encoder( (bbstate&NEW) ? 1 : 0 , ctxRoot); +#ifdef TRACE + DjVuPrintMessage("bbstate[bit=%d,band=%d] = %d\n", bit, band, bbstate); +#endif + } + + // code bucket bits + if (bbstate & NEW) + for (int buckno=0; buckno<nbucket; buckno++) + { + // Code bucket bit + if (bucketstate[buckno] & UNK) + { + // Context + int ctx = 0; +#ifndef NOCTX_BUCKET_UPPER + if (band>0) + { + int k = (fbucket+buckno)<<2; + const short *b = eblk.data(k>>4); + if (b) + { + k = k & 0xf; + if (b[k]) + ctx += 1; + if (b[k+1]) + ctx += 1; + if (b[k+2]) + ctx += 1; + if (ctx<3 && b[k+3]) + ctx += 1; + } + } +#endif +#ifndef NOCTX_BUCKET_ACTIVE + if (bbstate & ACTIVE) + ctx |= 4; +#endif + // Code + zp.encoder( (bucketstate[buckno]&NEW) ? 1 : 0, ctxBucket[band][ctx] ); +#ifdef TRACE + DjVuPrintMessage(" bucketstate[bit=%d,band=%d,buck=%d] = %d\n", + bit, band, buckno, bucketstate[buckno] & ~ZERO); +#endif + } + } + + // code new active coefficient (with their sign) + if (bbstate & NEW) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & NEW) + { + int i; +#ifndef NOCTX_EXPECT + int gotcha = 0; + const int maxgotcha = 7; + for (i=0; i<16; i++) + if (cstate[i] & UNK) + gotcha += 1; +#endif + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + // iterate within bucket + for (i=0; i<16; i++) + { + if (cstate[i] & UNK) + { + // Prepare context + int ctx = 0; +#ifndef NOCTX_EXPECT + if (gotcha>=maxgotcha) + ctx = maxgotcha; + else + ctx = gotcha; +#endif +#ifndef NOCTX_ACTIVE + if (bucketstate[buckno] & ACTIVE) + ctx |= 8; +#endif + // Code + zp.encoder( (cstate[i]&NEW) ? 1 : 0, ctxStart[ctx] ); + if (cstate[i] & NEW) + { + // Code sign + zp.IWencoder( (pcoeff[i]<0) ? 1 : 0 ); + // Set encoder state + if (band==0) + thres = quant_lo[i]; + epcoeff[i] = thres + (thres>>1); + } +#ifndef NOCTX_EXPECT + if (cstate[i] & NEW) + gotcha = 0; + else if (gotcha > 0) + gotcha -= 1; +#endif +#ifdef TRACE + DjVuPrintMessage(" coeffstate[bit=%d,band=%d,buck=%d,c=%d] = %d\n", + bit, band, buckno, i, cstate[i]); +#endif + } + } + } + } + + // code mantissa bits + if (bbstate & ACTIVE) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & ACTIVE) + { + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + for (int i=0; i<16; i++) + if (cstate[i] & ACTIVE) + { + // get coefficient + int coeff = pcoeff[i]; + int ecoeff = epcoeff[i]; + if (coeff < 0) + coeff = -coeff; + // get band zero thresholds + if (band == 0) + thres = quant_lo[i]; + // compute mantissa bit + int pix = 0; + if (coeff >= ecoeff) + pix = 1; + // encode second or lesser mantissa bit + if (ecoeff <= 3*thres) + zp.encoder(pix, ctxMant); + else + zp.IWencoder(!!pix); + // adjust epcoeff + epcoeff[i] = ecoeff - (pix ? 0 : thres) + (thres>>1); + } + } + } +} + +// IW44Image::Codec::estimate_decibel +// -- estimate encoding error (after code_slice) in decibels. +float +IW44Image::Codec::Encode::estimate_decibel(float frac) +{ + int i,j; + const float *q; + // Fill norm arrays + float norm_lo[16]; + float norm_hi[10]; + // -- lo coefficients + q = iw_norm; + for (i=j=0; i<4; j++) + norm_lo[i++] = *q++; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + norm_lo[i++] = *q; + q += 1; + // -- hi coefficients + norm_hi[0] = 0; + for (j=1; j<10; j++) + norm_hi[j] = *q++; + // Initialize mse array + float *xmse; + GPBuffer<float> gxmse(xmse,map.nb); + // Compute mse in each block + for (int blockno=0; blockno<map.nb; blockno++) + { + float mse = 0; + // Iterate over bands + for (int bandno=0; bandno<10; bandno++) + { + int fbucket = bandbuckets[bandno].start; + int nbucket = bandbuckets[bandno].size; + IW44Image::Block &blk = map.blocks[blockno]; + IW44Image::Block &eblk = emap.blocks[blockno]; + float norm = norm_hi[bandno]; + for (int buckno=0; buckno<nbucket; buckno++) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + if (pcoeff) + { + if (epcoeff) + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]<0 ? -pcoeff[i] : pcoeff[i]); + delta = delta - epcoeff[i]; + mse = mse + norm * delta * delta; + } + } + else + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]); + mse = mse + norm * delta * delta; + } + } + } + } + } + xmse[blockno] = mse / 1024; + } + // Compute partition point + int n = 0; + int m = map.nb - 1; + int p = (int)floor(m*(1.0-frac)+0.5); + p = (p>m ? m : (p<0 ? 0 : p)); + float pivot = 0; + // Partition array + while (n < p) + { + int l = n; + int h = m; + if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } + pivot = xmse[(l+h)/2]; + if (pivot < xmse[l]) { float tmp=pivot; pivot=xmse[l]; xmse[l]=tmp; } + if (pivot > xmse[h]) { float tmp=pivot; pivot=xmse[h]; xmse[h]=tmp; } + while (l < h) + { + if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } + while (xmse[l]<pivot || (xmse[l]==pivot && l<h)) l++; + while (xmse[h]>pivot) h--; + } + if (p>=l) + n = l; + else + m = l-1; + } + // Compute average mse + float mse = 0; + for (i=p; i<map.nb; i++) + mse = mse + xmse[i]; + mse = mse / (map.nb - p); + // Return + float factor = 255 << iw_shift; + float decibel = (float)(10.0 * log ( factor * factor / mse ) / 2.302585125); + return decibel; +} + + + + +////////////////////////////////////////////////////// +// IW44IMAGE ENCODING ROUTINES +////////////////////////////////////////////////////// + + +void +IW44Image::PrimaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(serial); + gbs->write8(slices); +} + +void +IW44Image::SecondaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(major); + gbs->write8(minor); +} + +void +IW44Image::TertiaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(xhi); + gbs->write8(xlo); + gbs->write8(yhi); + gbs->write8(ylo); + gbs->write8(crcbdelay); +} + + + +GP<IW44Image> +IW44Image::create_encode(const ImageType itype) +{ + switch(itype) + { + case COLOR: + return new IWPixmap::Encode(); + case GRAY: + return new IWBitmap::Encode(); + default: + return 0; + } +} + +GP<IW44Image> +IW44Image::create_encode(const GBitmap &bm, const GP<GBitmap> mask) +{ + IWBitmap::Encode *bit=new IWBitmap::Encode(); + GP<IW44Image> retval=bit; + bit->init(bm, mask); + return retval; +} + + +IWBitmap::Encode::Encode(void) +: IWBitmap(), ycodec_enc(0) +{} + +IWBitmap::Encode::~Encode() +{ + close_codec(); +} + +void +IWBitmap::Encode::init(const GBitmap &bm, const GP<GBitmap> gmask) +{ + // Free + close_codec(); + delete ymap; + ymap = 0; + // Init + int i, j; + int w = bm.columns(); + int h = bm.rows(); + int g = bm.get_grays()-1; + signed char *buffer; + GPBuffer<signed char> gbuffer(buffer,w*h); + // Prepare gray level conversion table + signed char bconv[256]; + for (i=0; i<256; i++) + bconv[i] = max(0,min(255,i*255/g)) - 128; + // Perform decomposition + // Prepare mask information + const signed char *msk8 = 0; + int mskrowsize = 0; + GBitmap *mask=gmask; + if (gmask) + { + msk8 = (const signed char*)((*mask)[0]); + mskrowsize = mask->rowsize(); + } + // Prepare a buffer of signed bytes + for (i=0; i<h; i++) + { + signed char *bufrow = buffer + i*w; + const unsigned char *bmrow = bm[i]; + for (j=0; j<w; j++) + bufrow[j] = bconv[bmrow[j]]; + } + // Create map + Map::Encode *eymap=new Map::Encode(w,h); + ymap = eymap; + eymap->create(buffer, w, msk8, mskrowsize); +} + +void +IWBitmap::Encode::close_codec(void) +{ + delete ycodec_enc; + ycodec_enc = 0; + IWBitmap::close_codec(); +} + +int +IWBitmap::Encode::encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parm) +{ + // Check + if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) + G_THROW( ERR_MSG("IW44Image.need_stop") ); + if (! ymap) + G_THROW( ERR_MSG("IW44Image.empty_object") ); + // Open codec + if (!ycodec_enc) + { + cslice = cserial = cbytes = 0; + ycodec_enc = new Codec::Encode(*ymap); + } + // Adjust cbytes + cbytes += sizeof(struct IW44Image::PrimaryHeader); + if (cserial == 0) + cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); + // Prepare zcoded slices + int flag = 1; + int nslices = 0; + GP<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk,"encode chunk",parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> gzp=ZPCodec::create(gmbs, true, true); + ZPCodec &zp=*gzp; + while (flag) + { + if (parm.decibels>0 && estdb>=parm.decibels) + break; + if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) + break; + if (parm.slices>0 && nslices+cslice>=parm.slices) + break; + DJVU_PROGRESS_RUN(chunk, (1+nslices-cslice)|0xf); + flag = ycodec_enc->code_slice(zp); + if (flag && parm.decibels>0.0) + if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) + estdb = ycodec_enc->estimate_decibel(db_frac); + nslices++; + } + } + // Write primary header + struct IW44Image::PrimaryHeader primary; + primary.serial = cserial; + primary.slices = nslices; + primary.encode(gbs); + // Write auxilliary headers + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.major = IWCODEC_MAJOR + 0x80; + secondary.minor = IWCODEC_MINOR; + secondary.encode(gbs); + struct IW44Image::TertiaryHeader tertiary; + tertiary.xhi = (ymap->iw >> 8) & 0xff; + tertiary.xlo = (ymap->iw >> 0) & 0xff; + tertiary.yhi = (ymap->ih >> 8) & 0xff; + tertiary.ylo = (ymap->ih >> 0) & 0xff; + tertiary.crcbdelay = 0; + tertiary.encode(gbs); + } + // Write slices + mbs.seek(0); + gbs->copy(mbs); + // Return + cbytes += mbs.tell(); + cslice += nslices; + cserial += 1; + return flag; +} + +void +IWBitmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) +{ + if (ycodec_enc) + G_THROW( ERR_MSG("IW44Image.left_open1") ); + int flag = 1; + iff.put_chunk("FORM:BM44", 1); + DJVU_PROGRESS_TASK(iff,"encode iff chunk",nchunks); + for (int i=0; flag && i<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("BM44"); + flag = encode_chunk(iff.get_bytestream(),parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +GP<IW44Image> +IW44Image::create_encode( + const GPixmap &pm, const GP<GBitmap> gmask, CRCBMode crcbmode) +{ + IWPixmap::Encode *pix=new IWPixmap::Encode(); + GP<IW44Image> retval=pix; + pix->init(pm, gmask,(IWPixmap::Encode::CRCBMode)crcbmode); + return retval; +} + +IWPixmap::Encode::Encode(void) +: IWPixmap(), ycodec_enc(0), cbcodec_enc(0), crcodec_enc(0) +{} + +IWPixmap::Encode::~Encode() +{ + close_codec(); +} + +void +IWPixmap::Encode::init(const GPixmap &pm, const GP<GBitmap> gmask, CRCBMode crcbmode) +{ + /* Free */ + close_codec(); + delete ymap; + delete cbmap; + delete crmap; + ymap = cbmap = crmap = 0; + /* Create */ + int w = pm.columns(); + int h = pm.rows(); + signed char *buffer; + GPBuffer<signed char> gbuffer(buffer,w*h); + // Create maps + Map::Encode *eymap = new Map::Encode(w,h); + ymap = eymap; + // Handle CRCB mode + switch (crcbmode) + { + case CRCBnone: crcb_half=1; crcb_delay=-1; break; + case CRCBhalf: crcb_half=1; crcb_delay=10; break; + case CRCBnormal: crcb_half=0; crcb_delay=10; break; + case CRCBfull: crcb_half=0; crcb_delay= 0; break; + } + // Prepare mask information + const signed char *msk8 = 0; + int mskrowsize = 0; + GBitmap *mask=gmask; + if (mask) + { + msk8 = (signed char const *)((*mask)[0]); + mskrowsize = mask->rowsize(); + } + // Fill buffer with luminance information + DJVU_PROGRESS_TASK(create,"initialize pixmap",3); + DJVU_PROGRESS_RUN(create,(crcb_delay>=0 ? 1 : 3)); + Transform::Encode::RGB_to_Y(pm[0], w, h, pm.rowsize(), buffer, w); + if (crcb_delay < 0) + { + // Stupid inversion for gray images + signed char *e = buffer + w*h; + for (signed char *b=buffer; b<e; b++) + *b = 255 - *b; + } + // Create YMAP + eymap->create(buffer, w, msk8, mskrowsize); + // Create chrominance maps + if (crcb_delay >= 0) + { + Map::Encode *ecbmap = new Map::Encode(w,h); + cbmap = ecbmap; + Map::Encode *ecrmap = new Map::Encode(w,h); + crmap = ecrmap; + // Process CB information + DJVU_PROGRESS_RUN(create,2); + Transform::Encode::RGB_to_Cb(pm[0], w, h, pm.rowsize(), buffer, w); + ecbmap->create(buffer, w, msk8, mskrowsize); + // Process CR information + DJVU_PROGRESS_RUN(create,3); + Transform::Encode::RGB_to_Cr(pm[0], w, h, pm.rowsize(), buffer, w); + ecrmap->create(buffer, w, msk8, mskrowsize); + // Perform chrominance reduction (CRCBhalf) + if (crcb_half) + { + ecbmap->slashres(2); + ecrmap->slashres(2); + } + } +} + +void +IWPixmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) +{ + if (ycodec_enc) + G_THROW( ERR_MSG("IW44Image.left_open3") ); + int flag = 1; + iff.put_chunk("FORM:PM44", 1); + DJVU_PROGRESS_TASK(iff,"encode pixmap chunk", nchunks); + for (int i=0; flag && i<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("PM44"); + flag = encode_chunk(iff.get_bytestream(), parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +void +IWPixmap::Encode::close_codec(void) +{ + delete ycodec_enc; + delete cbcodec_enc; + delete crcodec_enc; + ycodec_enc = crcodec_enc = cbcodec_enc = 0; + IWPixmap::close_codec(); +} + +int +IWPixmap::Encode::encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parm) +{ + // Check + if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) + G_THROW( ERR_MSG("IW44Image.need_stop2") ); + if (!ymap) + G_THROW( ERR_MSG("IW44Image.empty_object2") ); + // Open + if (!ycodec_enc) + { + cslice = cserial = cbytes = 0; + ycodec_enc = new Codec::Encode(*ymap); + if (crmap && cbmap) + { + cbcodec_enc = new Codec::Encode(*cbmap); + crcodec_enc = new Codec::Encode(*crmap); + } + } + + // Adjust cbytes + cbytes += sizeof(struct IW44Image::PrimaryHeader); + if (cserial == 0) + cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); + // Prepare zcodec slices + int flag = 1; + int nslices = 0; + GP<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk, "encode pixmap chunk", parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> gzp=ZPCodec::create(gmbs, true, true); + ZPCodec &zp=*gzp; + while (flag) + { + if (parm.decibels>0 && estdb>=parm.decibels) + break; + if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) + break; + if (parm.slices>0 && nslices+cslice>=parm.slices) + break; + DJVU_PROGRESS_RUN(chunk,(1+nslices-cslice)|0xf); + flag = ycodec_enc->code_slice(zp); + if (flag && parm.decibels>0) + if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) + estdb = ycodec_enc->estimate_decibel(db_frac); + if (crcodec_enc && cbcodec_enc && cslice+nslices>=crcb_delay) + { + flag |= cbcodec_enc->code_slice(zp); + flag |= crcodec_enc->code_slice(zp); + } + nslices++; + } + } + // Write primary header + struct IW44Image::PrimaryHeader primary; + primary.serial = cserial; + primary.slices = nslices; + primary.encode(gbs); + // Write secondary header + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.major = IWCODEC_MAJOR; + secondary.minor = IWCODEC_MINOR; + if (! (crmap && cbmap)) + secondary.major |= 0x80; + secondary.encode(gbs); + struct IW44Image::TertiaryHeader tertiary; + tertiary.xhi = (ymap->iw >> 8) & 0xff; + tertiary.xlo = (ymap->iw >> 0) & 0xff; + tertiary.yhi = (ymap->ih >> 8) & 0xff; + tertiary.ylo = (ymap->ih >> 0) & 0xff; + tertiary.crcbdelay = (crcb_half ? 0x00 : 0x80); + tertiary.crcbdelay |= (crcb_delay>=0 ? crcb_delay : 0x00); + tertiary.encode(gbs); + } + // Write slices + mbs.seek(0); + gbs->copy(mbs); + // Return + cbytes += mbs.tell(); + cslice += nslices; + cserial += 1; + return flag; +} + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::Encode::code_slice(ZPCodec &zp) +{ + // Check that code_slice can still run + if (curbit < 0) + return 0; + // Perform coding + if (! is_null_slice(curbit, curband)) + { + for (int blockno=0; blockno<map.nb; blockno++) + { + const int fbucket = bandbuckets[curband].start; + const int nbucket = bandbuckets[curband].size; + encode_buckets(zp, curbit, curband, + map.blocks[blockno], emap.blocks[blockno], + fbucket, nbucket); + } + } + return finish_code_slice(zp); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif // NEED_DECODER_ONLY + diff --git a/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp b/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp new file mode 100644 index 00000000..2cadf4f9 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44Image.cpp @@ -0,0 +1,1935 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44Image.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// - Author: Leon Bottou, 08/1998 + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split this file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#define IW44IMAGE_IMPLIMENTATION /* */ +// -------------------^ not my spelling mistake (Leon Bottou) + +#include "IW44Image.h" +#include "ZPCodec.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "IFFByteStream.h" +#include "GRect.h" + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "MMX.h" +#undef IWTRANSFORM_TIMER +#ifdef IWTRANSFORM_TIMER +#include "GOS.h" +#endif + +#include <assert.h> +#include <string.h> +#include <math.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#define IWALLOCSIZE 4080 +#define IWCODEC_MAJOR 1 +#define IWCODEC_MINOR 2 +#define DECIBEL_PRUNE 5.0 + + +////////////////////////////////////////////////////// +// WAVELET DECOMPOSITION CONSTANTS +////////////////////////////////////////////////////// + +// Parameters for IW44 wavelet. +// - iw_quant: quantization for all 16 sub-bands +// - iw_norm: norm of all wavelets (for db estimation) +// - iw_border: pixel border required to run filters +// - iw_shift: scale applied before decomposition + + +static const int iw_quant[16] = { + 0x004000, + 0x008000, 0x008000, 0x010000, + 0x010000, 0x010000, 0x020000, + 0x020000, 0x020000, 0x040000, + 0x040000, 0x040000, 0x080000, + 0x040000, 0x040000, 0x080000 +}; + +static const float iw_norm[16] = { + 2.627989e+03F, + 1.832893e+02F, 1.832959e+02F, 5.114690e+01F, + 4.583344e+01F, 4.583462e+01F, 1.279225e+01F, + 1.149671e+01F, 1.149712e+01F, 3.218888e+00F, + 2.999281e+00F, 2.999476e+00F, 8.733161e-01F, + 1.074451e+00F, 1.074511e+00F, 4.289318e-01F +}; + +static const int iw_border = 3; +static const int iw_shift = 6; +static const int iw_round = (1<<(iw_shift-1)); + +class IW44Image::Codec::Decode : public IW44Image::Codec +{ +public: + // Construction + Decode(IW44Image::Map &map) : Codec(map) {} + // Coding + virtual int code_slice(ZPCodec &zp); +}; + +////////////////////////////////////////////////////// +// MMX IMPLEMENTATION HELPERS +////////////////////////////////////////////////////// + + +// Note: +// MMX implementation for vertical transforms only. +// Speedup is basically related to faster memory transfer +// The IW44 transform is not CPU bound, it is memory bound. + +#ifdef MMX + +static const short w9[] = {9,9,9,9}; +static const short w1[] = {1,1,1,1}; +static const int d8[] = {8,8}; +static const int d16[] = {16,16}; + +static void +mmx_bv_1 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+16)>>5); + q ++; + } + while (q+3 < e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d16,mm0); + MMXar( paddd, d16,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 5,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 5,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( psubw, mm0,mm7); // MM7=[ p3-x3, p2-x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + + +static void +mmx_bv_2 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+8)>>4); + q ++; + } + while (q+3 < e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d8,mm0); + MMXar( paddd, d8,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 4,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 4,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( paddw, mm0,mm7); // MM7=[ p3+x3, p2+x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} +#endif /* MMX */ + +static void +filter_bv(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale*rowsize; + int s3 = s+s+s; + h = ((h-1)/scale)+1; + while (y-3 < h) + { + // 1-Lifting + { + short *q = p; + short *e = q+w; + if (y>=3 && y+3<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_bv_1(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + } + } + else if (y<h) + { + // Special cases + short *q1 = (y+1<h ? q+s : 0); + short *q3 = (y+3<h ? q+s3 : 0); + if (y>=3) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (int)q[-s3] + (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else if (y>=1) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else + { + while (q<e) + { + int a = (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q -= (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + } + } + // 2-Interpolation + { + short *q = p-s3; + short *e = q+w; + if (y>=6 && y<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_bv_2(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+8)>>4); + q += scale; + } + } + else if (y>=3) + { + // Special cases + short *q1 = (y-2<h ? q+s : q-s); + while (q<e) + { + int a = (int)q[-s] + (int)(*q1); + *q += ((a+1)>>1); + q += scale; + q1 += scale; + } + } + } + y += 2; + p += s+s; + } +} + +static void +filter_bh(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale; + int s3 = s+s+s; + rowsize *= scale; + while (y<h) + { + short *q = p; + short *e = p+w; + int a0=0, a1=0, a2=0, a3=0; + int b0=0, b1=0, b2=0, b3=0; + if (q<e) + { + // Special case: x=0 + if (q+s < e) + a2 = q[s]; + if (q+s3 < e) + a3 = q[s3]; + b2 = b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q += s+s; + } + if (q<e) + { + // Special case: x=2 + a0 = a1; + a1 = a2; + a2 = a3; + if (q+s3 < e) + a3 = q[s3]; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q += s+s; + } + if (q<e) + { + // Special case: x=4 + b1 = b2; + b2 = b3; + a0 = a1; + a1 = a2; + a2 = a3; + if (q+s3 < e) + a3 = q[s3]; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((b1+b2+1)>>1); + q += s+s; + } + while (q+s3 < e) + { + // Generic case + a0=a1; + a1=a2; + a2=a3; + a3=q[s3]; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+8) >> 4); + q += s+s; + } + while (q < e) + { + // Special case: w-3 <= x < w + a0=a1; + a1=a2; + a2=a3; + a3=0; + b0=b1; + b1=b2; + b2=b3; + b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+16) >> 5); + q[0] = b3; + q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+8) >> 4); + q += s+s; + } + while (q-s3 < e) + { + // Special case w <= x < w+3 + b0=b1; + b1=b2; + b2=b3; + if (q-s3 >= p) + q[-s3] = q[-s3] + ((b1+b2+1)>>1); + q += s+s; + } + y += scale; + p += rowsize; + } +} + + +////////////////////////////////////////////////////// +// REPRESENTATION OF WAVELET DECOMPOSED IMAGES +////////////////////////////////////////////////////// + + + +//--------------------------------------------------------------- +// Zig zag location in a 1024 liftblock. +// These numbers have been generated with the following program: +// +// int x=0, y=0; +// for (int i=0; i<5; i++) { +// x = (x<<1) | (n&1); n >>= 1; +// y = (y<<1) | (n&1); n >>= 1; +// } + + +static int zigzagloc[1024] = { + 0, 16, 512, 528, 8, 24, 520, 536, 256, 272, 768, 784, 264, 280, 776, 792, + 4, 20, 516, 532, 12, 28, 524, 540, 260, 276, 772, 788, 268, 284, 780, 796, + 128, 144, 640, 656, 136, 152, 648, 664, 384, 400, 896, 912, 392, 408, 904, 920, + 132, 148, 644, 660, 140, 156, 652, 668, 388, 404, 900, 916, 396, 412, 908, 924, + 2, 18, 514, 530, 10, 26, 522, 538, 258, 274, 770, 786, 266, 282, 778, 794, + 6, 22, 518, 534, 14, 30, 526, 542, 262, 278, 774, 790, 270, 286, 782, 798, + 130, 146, 642, 658, 138, 154, 650, 666, 386, 402, 898, 914, 394, 410, 906, 922, + 134, 150, 646, 662, 142, 158, 654, 670, 390, 406, 902, 918, 398, 414, 910, 926, + 64, 80, 576, 592, 72, 88, 584, 600, 320, 336, 832, 848, 328, 344, 840, 856, + 68, 84, 580, 596, 76, 92, 588, 604, 324, 340, 836, 852, 332, 348, 844, 860, + 192, 208, 704, 720, 200, 216, 712, 728, 448, 464, 960, 976, 456, 472, 968, 984, + 196, 212, 708, 724, 204, 220, 716, 732, 452, 468, 964, 980, 460, 476, 972, 988, + 66, 82, 578, 594, 74, 90, 586, 602, 322, 338, 834, 850, 330, 346, 842, 858, + 70, 86, 582, 598, 78, 94, 590, 606, 326, 342, 838, 854, 334, 350, 846, 862, + 194, 210, 706, 722, 202, 218, 714, 730, 450, 466, 962, 978, 458, 474, 970, 986, + 198, 214, 710, 726, 206, 222, 718, 734, 454, 470, 966, 982, 462, 478, 974, 990, // 255 + 1, 17, 513, 529, 9, 25, 521, 537, 257, 273, 769, 785, 265, 281, 777, 793, + 5, 21, 517, 533, 13, 29, 525, 541, 261, 277, 773, 789, 269, 285, 781, 797, + 129, 145, 641, 657, 137, 153, 649, 665, 385, 401, 897, 913, 393, 409, 905, 921, + 133, 149, 645, 661, 141, 157, 653, 669, 389, 405, 901, 917, 397, 413, 909, 925, + 3, 19, 515, 531, 11, 27, 523, 539, 259, 275, 771, 787, 267, 283, 779, 795, + 7, 23, 519, 535, 15, 31, 527, 543, 263, 279, 775, 791, 271, 287, 783, 799, + 131, 147, 643, 659, 139, 155, 651, 667, 387, 403, 899, 915, 395, 411, 907, 923, + 135, 151, 647, 663, 143, 159, 655, 671, 391, 407, 903, 919, 399, 415, 911, 927, + 65, 81, 577, 593, 73, 89, 585, 601, 321, 337, 833, 849, 329, 345, 841, 857, + 69, 85, 581, 597, 77, 93, 589, 605, 325, 341, 837, 853, 333, 349, 845, 861, + 193, 209, 705, 721, 201, 217, 713, 729, 449, 465, 961, 977, 457, 473, 969, 985, + 197, 213, 709, 725, 205, 221, 717, 733, 453, 469, 965, 981, 461, 477, 973, 989, + 67, 83, 579, 595, 75, 91, 587, 603, 323, 339, 835, 851, 331, 347, 843, 859, + 71, 87, 583, 599, 79, 95, 591, 607, 327, 343, 839, 855, 335, 351, 847, 863, + 195, 211, 707, 723, 203, 219, 715, 731, 451, 467, 963, 979, 459, 475, 971, 987, + 199, 215, 711, 727, 207, 223, 719, 735, 455, 471, 967, 983, 463, 479, 975, 991, // 511 + 32, 48, 544, 560, 40, 56, 552, 568, 288, 304, 800, 816, 296, 312, 808, 824, + 36, 52, 548, 564, 44, 60, 556, 572, 292, 308, 804, 820, 300, 316, 812, 828, + 160, 176, 672, 688, 168, 184, 680, 696, 416, 432, 928, 944, 424, 440, 936, 952, + 164, 180, 676, 692, 172, 188, 684, 700, 420, 436, 932, 948, 428, 444, 940, 956, + 34, 50, 546, 562, 42, 58, 554, 570, 290, 306, 802, 818, 298, 314, 810, 826, + 38, 54, 550, 566, 46, 62, 558, 574, 294, 310, 806, 822, 302, 318, 814, 830, + 162, 178, 674, 690, 170, 186, 682, 698, 418, 434, 930, 946, 426, 442, 938, 954, + 166, 182, 678, 694, 174, 190, 686, 702, 422, 438, 934, 950, 430, 446, 942, 958, + 96, 112, 608, 624, 104, 120, 616, 632, 352, 368, 864, 880, 360, 376, 872, 888, + 100, 116, 612, 628, 108, 124, 620, 636, 356, 372, 868, 884, 364, 380, 876, 892, + 224, 240, 736, 752, 232, 248, 744, 760, 480, 496, 992,1008, 488, 504,1000,1016, + 228, 244, 740, 756, 236, 252, 748, 764, 484, 500, 996,1012, 492, 508,1004,1020, + 98, 114, 610, 626, 106, 122, 618, 634, 354, 370, 866, 882, 362, 378, 874, 890, + 102, 118, 614, 630, 110, 126, 622, 638, 358, 374, 870, 886, 366, 382, 878, 894, + 226, 242, 738, 754, 234, 250, 746, 762, 482, 498, 994,1010, 490, 506,1002,1018, + 230, 246, 742, 758, 238, 254, 750, 766, 486, 502, 998,1014, 494, 510,1006,1022, // 767 + 33, 49, 545, 561, 41, 57, 553, 569, 289, 305, 801, 817, 297, 313, 809, 825, + 37, 53, 549, 565, 45, 61, 557, 573, 293, 309, 805, 821, 301, 317, 813, 829, + 161, 177, 673, 689, 169, 185, 681, 697, 417, 433, 929, 945, 425, 441, 937, 953, + 165, 181, 677, 693, 173, 189, 685, 701, 421, 437, 933, 949, 429, 445, 941, 957, + 35, 51, 547, 563, 43, 59, 555, 571, 291, 307, 803, 819, 299, 315, 811, 827, + 39, 55, 551, 567, 47, 63, 559, 575, 295, 311, 807, 823, 303, 319, 815, 831, + 163, 179, 675, 691, 171, 187, 683, 699, 419, 435, 931, 947, 427, 443, 939, 955, + 167, 183, 679, 695, 175, 191, 687, 703, 423, 439, 935, 951, 431, 447, 943, 959, + 97, 113, 609, 625, 105, 121, 617, 633, 353, 369, 865, 881, 361, 377, 873, 889, + 101, 117, 613, 629, 109, 125, 621, 637, 357, 373, 869, 885, 365, 381, 877, 893, + 225, 241, 737, 753, 233, 249, 745, 761, 481, 497, 993,1009, 489, 505,1001,1017, + 229, 245, 741, 757, 237, 253, 749, 765, 485, 501, 997,1013, 493, 509,1005,1021, + 99, 115, 611, 627, 107, 123, 619, 635, 355, 371, 867, 883, 363, 379, 875, 891, + 103, 119, 615, 631, 111, 127, 623, 639, 359, 375, 871, 887, 367, 383, 879, 895, + 227, 243, 739, 755, 235, 251, 747, 763, 483, 499, 995,1011, 491, 507,1003,1019, + 231, 247, 743, 759, 239, 255, 751, 767, 487, 503, 999,1015, 495, 511,1007,1023, // 1023 +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Alloc [declaration] + +struct IW44Image::Alloc // DJVU_CLASS +{ + Alloc *next; + short data[IWALLOCSIZE]; +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Block [implementation] + + +IW44Image::Block::Block(void) +{ + pdata[0] = pdata[1] = pdata[2] = pdata[3] = 0; +} + +void +IW44Image::Block::zero(int n) +{ + if (pdata[n>>4]) + pdata[n>>4][n&15] = 0; +} + +void +IW44Image::Block::read_liftblock(const short *coeff, IW44Image::Map *map) +{ + int n=0; + for (int n1=0; n1<64; n1++) + { + short *d = data(n1,map); + for (int n2=0; n2<16; n2++,n++) + d[n2] = coeff[zigzagloc[n]]; + } +} + +void +IW44Image::Block::write_liftblock(short *coeff, int bmin, int bmax) const +{ + int n = bmin<<4; + memset(coeff, 0, 1024*sizeof(short)); + for (int n1=bmin; n1<bmax; n1++) + { + const short *d = data(n1); + if (d == 0) + n += 16; + else + for (int n2=0; n2<16; n2++,n++) + coeff[zigzagloc[n]] = d[n2]; + } +} + +//--------------------------------------------------------------- +// *** Class IW44Image::Map [implementation] + + +IW44Image::Map::Map(int w, int h) + : blocks(0), iw(w), ih(h), chain(0) +{ + bw = (w+0x20-1) & ~0x1f; + bh = (h+0x20-1) & ~0x1f; + nb = (bw * bh) / (32 * 32); + blocks = new IW44Image::Block[nb]; + top = IWALLOCSIZE; +} + +IW44Image::Map::~Map() +{ + while (chain) + { + IW44Image::Alloc *next = chain->next; + delete chain; + chain = next; + } + delete [] blocks; +} + +short * +IW44Image::Map::alloc(int n) +{ + if (top+n > IWALLOCSIZE) + { + IW44Image::Alloc *n = new IW44Image::Alloc; + n->next = chain; + chain = n; + top = 0; + } + short *ans = chain->data + top; + top += n; + memset((void*)ans, 0, sizeof(short)*n); + return ans; +} + +short ** +IW44Image::Map::allocp(int n) +{ + // Allocate enough room for pointers plus alignment + short *p = alloc( (n+1) * sizeof(short*) / sizeof(short) ); + // Align on pointer size + while ( ((long)p) & (sizeof(short*)-1) ) + p += 1; + // Cast and return + return (short**)p; +} + +int +IW44Image::Map::get_bucket_count(void) const +{ + int buckets = 0; + for (int blockno=0; blockno<nb; blockno++) + for (int buckno=0; buckno<64; buckno++) + if (blocks[blockno].data(buckno)) + buckets += 1; + return buckets; +} + +unsigned int +IW44Image::Map::get_memory_usage(void) const +{ + unsigned int usage = sizeof(Map); + usage += sizeof(IW44Image::Block) * nb; + for (IW44Image::Alloc *n = chain; n; n=n->next) + usage += sizeof(IW44Image::Alloc); + return usage; +} + + + + +void +IW44Image::Map::image(signed char *img8, int rowsize, int pixsep, int fast) +{ + // Allocate reconstruction buffer + short *data16; + GPBuffer<short> gdata16(data16,bw*bh); + // Copy coefficients + int i; + short *p = data16; + const IW44Image::Block *block = blocks; + for (i=0; i<bh; i+=32) + { + for (int j=0; j<bw; j+=32) + { + short liftblock[1024]; + // transfer into IW44Image::Block (apply zigzag and scaling) + block->write_liftblock(liftblock); + block++; + // transfer into coefficient matrix at (p+j) + short *pp = p + j; + short *pl = liftblock; + for (int ii=0; ii<32; ii++, pp+=bw,pl+=32) + memcpy((void*)pp, (void*)pl, 32*sizeof(short)); + } + // next row of blocks + p += 32*bw; + } + // Reconstruction + if (fast) + { + IW44Image::Transform::Decode::backward(data16, iw, ih, bw, 32, 2); + p = data16; + for (i=0; i<bh; i+=2,p+=bw) + for (int jj=0; jj<bw; jj+=2,p+=2) + p[bw] = p[bw+1] = p[1] = p[0]; + } + else + { + IW44Image::Transform::Decode::backward(data16, iw, ih, bw, 32, 1); + } + // Copy result into image + p = data16; + signed char *row = img8; + for (i=0; i<ih; i++) + { + signed char *pix = row; + for (int j=0; j<iw; j+=1,pix+=pixsep) + { + int x = (p[j] + iw_round) >> iw_shift; + if (x < -128) + x = -128; + else if (x > 127) + x = 127; + *pix = x; + } + row += rowsize; + p += bw; + } +} + +void +IW44Image::Map::image(int subsample, const GRect &rect, + signed char *img8, int rowsize, int pixsep, int fast) +{ + int i; + // Compute number of decomposition levels + int nlevel = 0; + while (nlevel<5 && (32>>nlevel)>subsample) + nlevel += 1; + int boxsize = 1<<nlevel; + // Parameter check + if (subsample!=(32>>nlevel)) + G_THROW( ERR_MSG("IW44Image.sample_factor") ); + if (rect.isempty()) + G_THROW( ERR_MSG("IW44Image.empty_rect") ); + GRect irect(0,0,(iw+subsample-1)/subsample,(ih+subsample-1)/subsample); + if (rect.xmin<0 || rect.ymin<0 || rect.xmax>irect.xmax || rect.ymax>irect.ymax) + G_THROW( ERR_MSG("IW44Image.bad_rect") ); + // Multiresolution rectangles + // -- needed[i] tells which coeffs are required for the next step + // -- recomp[i] tells which coeffs need to be computed at this level + GRect needed[8]; + GRect recomp[8]; + int r = 1; + needed[nlevel] = rect; + recomp[nlevel] = rect; + for (i=nlevel-1; i>=0; i--) + { + needed[i] = recomp[i+1]; + needed[i].inflate(iw_border*r, iw_border*r); + needed[i].intersect(needed[i], irect); + r += r; + recomp[i].xmin = (needed[i].xmin + r-1) & ~(r-1); + recomp[i].xmax = (needed[i].xmax) & ~(r-1); + recomp[i].ymin = (needed[i].ymin + r-1) & ~(r-1); + recomp[i].ymax = (needed[i].ymax) & ~(r-1); + } + // Working rectangle + // -- a rectangle large enough to hold all the data + GRect work; + work.xmin = (needed[0].xmin) & ~(boxsize-1); + work.ymin = (needed[0].ymin) & ~(boxsize-1); + work.xmax = ((needed[0].xmax-1) & ~(boxsize-1) ) + boxsize; + work.ymax = ((needed[0].ymax-1) & ~(boxsize-1) ) + boxsize; + // -- allocate work buffer + int dataw = work.xmax - work.xmin; // Note: cannot use inline width() or height() + int datah = work.ymax - work.ymin; // because Intel C++ compiler optimizes it wrong ! + short *data; + GPBuffer<short> gdata(data,dataw*datah); + // Fill working rectangle + // -- loop over liftblocks rows + short *ldata = data; + int blkw = (bw>>5); + const IW44Image::Block *lblock = blocks + (work.ymin>>nlevel)*blkw + (work.xmin>>nlevel); + for (int by=work.ymin; by<work.ymax; by+=boxsize) + { + // -- loop over liftblocks in row + const IW44Image::Block *block = lblock; + short *rdata = ldata; + for (int bx=work.xmin; bx<work.xmax; bx+=boxsize) + { + // -- decide how much to load + int mlevel = nlevel; + if (nlevel>2) + if (bx+31<needed[2].xmin || bx>needed[2].xmax || + by+31<needed[2].ymin || by>needed[2].ymax ) + mlevel = 2; + int bmax = ((1<<(mlevel+mlevel))+15)>>4; + int ppinc = (1<<(nlevel-mlevel)); + int ppmod1 = (dataw<<(nlevel-mlevel)); + int ttmod0 = (32 >> mlevel); + int ttmod1 = (ttmod0 << 5); + // -- get current block + short liftblock[1024]; + block->write_liftblock(liftblock, 0, bmax ); + // -- copy liftblock into image + short *tt = liftblock; + short *pp = rdata; + for (int ii=0; ii<boxsize; ii+=ppinc,pp+=ppmod1,tt+=ttmod1-32) + for (int jj=0; jj<boxsize; jj+=ppinc,tt+=ttmod0) + pp[jj] = *tt; + // -- next block in row + rdata += boxsize; + block += 1; + } + // -- next row of blocks + ldata += dataw << nlevel; + lblock += blkw; + } + // Perform reconstruction + r = boxsize; + for (i=0; i<nlevel; i++) + { + GRect comp = needed[i]; + comp.xmin = comp.xmin & ~(r-1); + comp.ymin = comp.ymin & ~(r-1); + comp.translate(-work.xmin, -work.ymin); + // Fast mode shortcuts finer resolution + if (fast && i>=4) + { + short *pp = data + comp.ymin*dataw; + for (int ii=comp.ymin; ii<comp.ymax; ii+=2, pp+=dataw+dataw) + for (int jj=comp.xmin; jj<comp.xmax; jj+=2) + pp[jj+dataw] = pp[jj+dataw+1] = pp[jj+1] = pp[jj]; + break; + } + else + { + short *pp = data + comp.ymin*dataw + comp.xmin; + IW44Image::Transform::Decode::backward(pp, comp.width(), comp.height(), dataw, r, r>>1); + } + r = r>>1; + } + // Copy result into image + GRect nrect = rect; + nrect.translate(-work.xmin, -work.ymin); + short *p = data + nrect.ymin*dataw; + signed char *row = img8; + for (i=nrect.ymin; i<nrect.ymax; i++) + { + int j; + signed char *pix = row; + for (j=nrect.xmin; j<nrect.xmax; j+=1,pix+=pixsep) + { + int x = (p[j] + iw_round) >> iw_shift; + if (x < -128) + x = -128; + else if (x > 127) + x = 127; + *pix = x; + } + row += rowsize; + p += dataw; + } +} + + + + +////////////////////////////////////////////////////// +// ENCODING/DECODING WAVELET COEFFICIENTS +// USING HIERARCHICAL SET DIFFERENCE +////////////////////////////////////////////////////// + + +//----------------------------------------------- +// Class IW44Image::Codec [implementation] +// Maintains information shared while encoding or decoding + + +// Constant + +static const struct { int start; int size; } +bandbuckets[] = +{ + // Code first bucket and number of buckets in each band + { 0, 1 }, // -- band zero contains all lores info + { 1, 1 }, { 2, 1 }, { 3, 1 }, + { 4, 4 }, { 8, 4 }, { 12,4 }, + { 16,16 }, { 32,16 }, { 48,16 }, +}; + + +// IW44Image::Codec constructor + +IW44Image::Codec::Codec(IW44Image::Map &xmap) + : map(xmap), + curband(0), + curbit(1) +{ + // Initialize quantification + int j; + int i = 0; + const int *q = iw_quant; + // -- lo coefficients + for (j=0; i<4; j++) + quant_lo[i++] = *q++; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + for (j=0; j<4; j++) + quant_lo[i++] = *q; + q += 1; + // -- hi coefficients + quant_hi[0] = 0; + for (j=1; j<10; j++) + quant_hi[j] = *q++; + // Initialize coding contexts + memset((void*)ctxStart, 0, sizeof(ctxStart)); + memset((void*)ctxBucket, 0, sizeof(ctxBucket)); + ctxMant = 0; + ctxRoot = 0; +} + + +// IW44Image::Codec destructor + +IW44Image::Codec::~Codec() {} + +// is_null_slice +// -- check if data can be produced for this band/mask +// -- also fills the sure_zero array + +int +IW44Image::Codec::is_null_slice(int bit, int band) +{ + if (band == 0) + { + int is_null = 1; + for (int i=0; i<16; i++) + { + int threshold = quant_lo[i]; + coeffstate[i] = ZERO; + if (threshold>0 && threshold<0x8000) + { + coeffstate[i] = UNK; + is_null = 0; + } + } + return is_null; + } + else + { + int threshold = quant_hi[band]; + return (! (threshold>0 && threshold<0x8000)); + } +} + + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::Decode::code_slice(ZPCodec &zp) +{ + // Check that code_slice can still run + if (curbit < 0) + return 0; + // Perform coding + if (! is_null_slice(curbit, curband)) + { + for (int blockno=0; blockno<map.nb; blockno++) + { + int fbucket = bandbuckets[curband].start; + int nbucket = bandbuckets[curband].size; + decode_buckets(zp, curbit, curband, + map.blocks[blockno], + fbucket, nbucket); + } + } + return finish_code_slice(zp); +} + +// code_slice +// -- read/write a slice of datafile + +int +IW44Image::Codec::finish_code_slice(ZPCodec &zp) +{ + // Reduce quantization threshold + quant_hi[curband] = quant_hi[curband] >> 1; + if (curband == 0) + for (int i=0; i<16; i++) + quant_lo[i] = quant_lo[i] >> 1; + // Proceed to the next slice + if (++curband >= (int)(sizeof(bandbuckets)/sizeof(bandbuckets[0]))) + { + curband = 0; + curbit += 1; + if (quant_hi[(sizeof(bandbuckets)/sizeof(bandbuckets[0]))-1] == 0) + { + // All quantization thresholds are null + curbit = -1; + return 0; + } + } + return 1; +} + +// decode_prepare +// -- prepare the states before decoding buckets + +int +IW44Image::Codec::decode_prepare(int fbucket, int nbucket, IW44Image::Block &blk) +{ + int bbstate = 0; + char *cstate = coeffstate; + if (fbucket) + { + // Band other than zero + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + { + int bstatetmp = 0; + const short *pcoeff = blk.data(fbucket+buckno); + if (! pcoeff) + { + // cstate[0..15] will be filled later + bstatetmp = UNK; + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if (pcoeff[i]) + cstatetmp = ACTIVE; + cstate[i] = cstatetmp; + bstatetmp |= cstatetmp; + } + } + bucketstate[buckno] = bstatetmp; + bbstate |= bstatetmp; + } + } + else + { + // Band zero ( fbucket==0 implies band==zero and nbucket==1 ) + const short *pcoeff = blk.data(0); + if (! pcoeff) + { + // cstate[0..15] will be filled later + bbstate = UNK; + } + else + { + for (int i=0; i<16; i++) + { + int cstatetmp = cstate[i]; + if (cstatetmp != ZERO) + { + cstatetmp = UNK; + if (pcoeff[i]) + cstatetmp = ACTIVE; + } + cstate[i] = cstatetmp; + bbstate |= cstatetmp; + } + } + bucketstate[0] = bbstate; + } + return bbstate; +} + + +// decode_buckets +// -- code a sequence of buckets in a given block + +void +IW44Image::Codec::decode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, + int fbucket, int nbucket) +{ + // compute state of all coefficients in all buckets + int bbstate = decode_prepare(fbucket, nbucket, blk); + // code root bit + if ((nbucket<16) || (bbstate&ACTIVE)) + { + bbstate |= NEW; + } + else if (bbstate & UNK) + { + if (zp.decoder(ctxRoot)) + bbstate |= NEW; +#ifdef TRACE + DjVuPrintMessage("bbstate[bit=%d,band=%d] = %d\n", bit, band, bbstate); +#endif + } + + // code bucket bits + if (bbstate & NEW) + for (int buckno=0; buckno<nbucket; buckno++) + { + // Code bucket bit + if (bucketstate[buckno] & UNK) + { + // Context + int ctx = 0; +#ifndef NOCTX_BUCKET_UPPER + if (band>0) + { + int k = (fbucket+buckno)<<2; + const short *b = blk.data(k>>4); + if (b) + { + k = k & 0xf; + if (b[k]) + ctx += 1; + if (b[k+1]) + ctx += 1; + if (b[k+2]) + ctx += 1; + if (ctx<3 && b[k+3]) + ctx += 1; + } + } +#endif // NOCTX_BUCKET_UPPER +#ifndef NOCTX_BUCKET_ACTIVE + if (bbstate & ACTIVE) + ctx |= 4; +#endif + // Code + if (zp.decoder( ctxBucket[band][ctx] )) + bucketstate[buckno] |= NEW; +#ifdef TRACE + DjVuPrintMessage(" bucketstate[bit=%d,band=%d,buck=%d] = %d\n", + bit, band, buckno, bucketstate[buckno]); +#endif + } + } + + // code new active coefficient (with their sign) + if (bbstate & NEW) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & NEW) + { + int i; + short *pcoeff = (short*)blk.data(fbucket+buckno); + if (!pcoeff) + { + pcoeff = blk.data(fbucket+buckno, &map); + // time to fill cstate[0..15] + if (fbucket == 0) // band zero + { + for (i=0; i<16; i++) + if (cstate[i] != ZERO) + cstate[i] = UNK; + } + else + { + for (i=0; i<16; i++) + cstate[i] = UNK; + } + } +#ifndef NOCTX_EXPECT + int gotcha = 0; + const int maxgotcha = 7; + for (i=0; i<16; i++) + if (cstate[i] & UNK) + gotcha += 1; +#endif + for (i=0; i<16; i++) + { + if (cstate[i] & UNK) + { + // find lores threshold + if (band == 0) + thres = quant_lo[i]; + // prepare context + int ctx = 0; +#ifndef NOCTX_EXPECT + if (gotcha>=maxgotcha) + ctx = maxgotcha; + else + ctx = gotcha; +#endif +#ifndef NOCTX_ACTIVE + if (bucketstate[buckno] & ACTIVE) + ctx |= 8; +#endif + // code difference bit + if (zp.decoder( ctxStart[ctx] )) + { + cstate[i] |= NEW; + int halfthres = thres>>1; + int coeff = thres+halfthres-(halfthres>>2); + if (zp.IWdecoder()) + pcoeff[i] = -coeff; + else + pcoeff[i] = coeff; + } +#ifndef NOCTX_EXPECT + if (cstate[i] & NEW) + gotcha = 0; + else if (gotcha > 0) + gotcha -= 1; +#endif +#ifdef TRACE + DjVuPrintMessage(" coeffstate[bit=%d,band=%d,buck=%d,c=%d] = %d\n", + bit, band, buckno, i, cstate[i]); +#endif + } + } + } + } + + // code mantissa bits + if (bbstate & ACTIVE) + { + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & ACTIVE) + { + short *pcoeff = (short*)blk.data(fbucket+buckno); + for (int i=0; i<16; i++) + if (cstate[i] & ACTIVE) + { + int coeff = pcoeff[i]; + if (coeff < 0) + coeff = -coeff; + // find lores threshold + if (band == 0) + thres = quant_lo[i]; + // adjust coefficient + if (coeff <= 3*thres) + { + // second mantissa bit + coeff = coeff + (thres>>2); + if (zp.decoder(ctxMant)) + coeff = coeff + (thres>>1); + else + coeff = coeff - thres + (thres>>1); + } + else + { + if (zp.IWdecoder()) + coeff = coeff + (thres>>1); + else + coeff = coeff - thres + (thres>>1); + } + // store coefficient + if (pcoeff[i] > 0) + pcoeff[i] = coeff; + else + pcoeff[i] = -coeff; + } + } + } +} + + +////////////////////////////////////////////////////// +// UTILITIES +////////////////////////////////////////////////////// + + +#ifdef min +#undef min +#endif +static inline int +min(const int x, const int y) +{ + return (x <= y) ? x : y; +} + +#ifdef max +#undef max +#endif +static inline int +max(const int x, const int y) +{ + return (y <= x) ? x : y; +} + + +void +IW44Image::PrimaryHeader::decode(GP<ByteStream> gbs) +{ + serial = gbs->read8(); + slices = gbs->read8(); +} + +void +IW44Image::SecondaryHeader::decode(GP<ByteStream> gbs) +{ + major = gbs->read8(); + minor = gbs->read8(); +} + +void +IW44Image::TertiaryHeader::decode(GP<ByteStream> gbs, int major, int minor) +{ + xhi = gbs->read8(); + xlo = gbs->read8(); + yhi = gbs->read8(); + ylo = gbs->read8(); + crcbdelay = 0; + if (major== 1 && minor>=2) + crcbdelay = gbs->read8(); +} + + + +////////////////////////////////////////////////////// +// CLASS IW44Image +////////////////////////////////////////////////////// + +IW44Image::IW44Image(void) + : db_frac(1.0), + ymap(0), cbmap(0), crmap(0), + cslice(0), cserial(0), cbytes(0) +{} + +IW44Image::~IW44Image() +{ + delete ymap; + delete cbmap; + delete crmap; +} + +GP<IW44Image> +IW44Image::create_decode(const ImageType itype) +{ + switch(itype) + { + case COLOR: + return new IWPixmap(); + case GRAY: + return new IWBitmap(); + default: + return 0; + } +} + +int +IW44Image::encode_chunk(GP<ByteStream>, const IWEncoderParms &) +{ + G_THROW( ERR_MSG("IW44Image.codec_open2") ); + return 0; +} + +void +IW44Image::encode_iff(IFFByteStream &, int nchunks, const IWEncoderParms *) +{ + G_THROW( ERR_MSG("IW44Image.codec_open2") ); +} + + +void +IWBitmap::close_codec(void) +{ + delete ycodec; + ycodec = 0; + cslice = cbytes = cserial = 0; +} + +void +IWPixmap::close_codec(void) +{ + delete ycodec; + delete cbcodec; + delete crcodec; + ycodec = crcodec = cbcodec = 0; + cslice = cbytes = cserial = 0; +} + +int +IW44Image::get_width(void) const +{ + return (ymap)?(ymap->iw):0; +} + +int +IW44Image::get_height(void) const +{ + return (ymap)?(ymap->ih):0; +} + + +////////////////////////////////////////////////////// +// CLASS IWBITMAP +////////////////////////////////////////////////////// + +IWBitmap::IWBitmap(void ) +: IW44Image(), ycodec(0) +{} + +IWBitmap::~IWBitmap() +{ + close_codec(); +} + +int +IWBitmap::get_percent_memory(void) const +{ + int buckets = 0; + int maximum = 0; + if (ymap) + { + buckets += ymap->get_bucket_count(); + maximum += 64 * ymap->nb; + } + return 100*buckets/ (maximum ? maximum : 1); +} + +unsigned int +IWBitmap::get_memory_usage(void) const +{ + unsigned int usage = sizeof(GBitmap); + if (ymap) + usage += ymap->get_memory_usage(); + return usage; +} + + +GP<GBitmap> +IWBitmap::get_bitmap(void) +{ + // Check presence of data + if (ymap == 0) + return 0; + // Perform wavelet reconstruction + int w = ymap->iw; + int h = ymap->ih; + GP<GBitmap> pbm = GBitmap::create(h, w); + ymap->image((signed char*)(*pbm)[0],pbm->rowsize()); + // Shift image data + for (int i=0; i<h; i++) + { + unsigned char *urow = (*pbm)[i]; + signed char *srow = (signed char*)urow; + for (int j=0; j<w; j++) + urow[j] = (int)(srow[j]) + 128; + } + pbm->set_grays(256); + return pbm; +} + + +GP<GBitmap> +IWBitmap::get_bitmap(int subsample, const GRect &rect) +{ + if (ymap == 0) + return 0; + // Allocate bitmap + int w = rect.width(); + int h = rect.height(); + GP<GBitmap> pbm = GBitmap::create(h,w); + ymap->image(subsample, rect, (signed char*)(*pbm)[0],pbm->rowsize()); + // Shift image data + for (int i=0; i<h; i++) + { + unsigned char *urow = (*pbm)[i]; + signed char *srow = (signed char*)urow; + for (int j=0; j<w; j++) + urow[j] = (int)(srow[j]) + 128; + } + pbm->set_grays(256); + return pbm; +} + + +int +IWBitmap::decode_chunk(GP<ByteStream> gbs) +{ + // Open + if (! ycodec) + { + cslice = cserial = 0; + delete ymap; + ymap = 0; + } + // Read primary header + struct IW44Image::PrimaryHeader primary; + primary.decode(gbs); + if (primary.serial != cserial) + G_THROW( ERR_MSG("IW44Image.wrong_serial") ); + int nslices = cslice + primary.slices; + // Read auxilliary headers + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.decode(gbs); + if ((secondary.major & 0x7f) != IWCODEC_MAJOR) + G_THROW( ERR_MSG("IW44Image.incompat_codec") ); + if (secondary.minor > IWCODEC_MINOR) + G_THROW( ERR_MSG("IW44Image.recent_codec") ); + // Read tertiary header + struct IW44Image::TertiaryHeader tertiary; + tertiary.decode(gbs, secondary.major & 0x7f, secondary.minor); + if (! (secondary.major & 0x80)) + G_THROW( ERR_MSG("IW44Image.has_color") ); + // Create ymap and ycodec + int w = (tertiary.xhi << 8) | tertiary.xlo; + int h = (tertiary.yhi << 8) | tertiary.ylo; + assert(! ymap); + ymap = new Map(w, h); + assert(! ycodec); + ycodec = new Codec::Decode(*ymap); + } + // Read data + assert(ymap); + assert(ycodec); + GP<ZPCodec> gzp=ZPCodec::create(gbs, false, true); + ZPCodec &zp=*gzp; + int flag = 1; + while (flag && cslice<nslices) + { + flag = ycodec->code_slice(zp); + cslice++; + } + // Return + cserial += 1; + return nslices; +} + +void +IWBitmap::parm_dbfrac(float frac) +{ + if (frac>0 && frac<=1) + db_frac = frac; + else + G_THROW( ERR_MSG("IW44Image.param_range") ); +} + + +int +IWBitmap::get_serial(void) +{ + return cserial; +} + +void +IWBitmap::decode_iff(IFFByteStream &iff, int maxchunks) +{ + if (ycodec) + G_THROW( ERR_MSG("IW44Image.left_open2") ); + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid != "FORM:BM44") + G_THROW( ERR_MSG("IW44Image.corrupt_BM44") ); + while (--maxchunks>=0 && iff.get_chunk(chkid)) + { + if (chkid == "BM44") + decode_chunk(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + + + + +////////////////////////////////////////////////////// +// CLASS IWENCODERPARMS +////////////////////////////////////////////////////// + + +IWEncoderParms::IWEncoderParms(void) +{ + // Zero represent default values + memset((void*)this, 0, sizeof(IWEncoderParms)); +} + + + + + +////////////////////////////////////////////////////// +// CLASS IWPIXMAP +////////////////////////////////////////////////////// + + +IWPixmap::IWPixmap(void) +: IW44Image(), crcb_delay(10), crcb_half(0), ycodec(0), cbcodec(0), crcodec(0) +{} + +IWPixmap::~IWPixmap() +{ + close_codec(); +} + +int +IWPixmap::get_percent_memory(void) const +{ + int buckets = 0; + int maximum = 0; + if (ymap) + { + buckets += ymap->get_bucket_count(); + maximum += 64*ymap->nb; + } + if (cbmap) + { + buckets += cbmap->get_bucket_count(); + maximum += 64*cbmap->nb; + } + if (crmap) + { + buckets += crmap->get_bucket_count(); + maximum += 64*crmap->nb; + } + return 100*buckets/ (maximum ? maximum : 1); +} + +unsigned int +IWPixmap::get_memory_usage(void) const +{ + unsigned int usage = sizeof(GPixmap); + if (ymap) + usage += ymap->get_memory_usage(); + if (cbmap) + usage += cbmap->get_memory_usage(); + if (crmap) + usage += crmap->get_memory_usage(); + return usage; +} + + +GP<GPixmap> +IWPixmap::get_pixmap(void) +{ + // Check presence of data + if (ymap == 0) + return 0; + // Allocate pixmap + int w = ymap->iw; + int h = ymap->ih; + GP<GPixmap> ppm = GPixmap::create(h, w); + // Perform wavelet reconstruction + signed char *ptr = (signed char*) (*ppm)[0]; + int rowsep = ppm->rowsize() * sizeof(GPixel); + int pixsep = sizeof(GPixel); + ymap->image(ptr, rowsep, pixsep); + if (crmap && cbmap && crcb_delay >= 0) + { + cbmap->image(ptr+1, rowsep, pixsep, crcb_half); + crmap->image(ptr+2, rowsep, pixsep, crcb_half); + } + // Convert image data to RGB + if (crmap && cbmap && crcb_delay >= 0) + { + Transform::Decode::YCbCr_to_RGB((*ppm)[0], w, h, ppm->rowsize()); + } + else + { + for (int i=0; i<h; i++) + { + GPixel *pixrow = (*ppm)[i]; + for (int j=0; j<w; j++, pixrow++) + pixrow->b = pixrow->g = pixrow->r + = 127 - (int)(((signed char*)pixrow)[0]); + } + } + // Return + return ppm; +} + + + +GP<GPixmap> +IWPixmap::get_pixmap(int subsample, const GRect &rect) +{ + if (ymap == 0) + return 0; + // Allocate + int w = rect.width(); + int h = rect.height(); + GP<GPixmap> ppm = GPixmap::create(h,w); + // Perform wavelet reconstruction + signed char *ptr = (signed char*) (*ppm)[0]; + int rowsep = ppm->rowsize() * sizeof(GPixel); + int pixsep = sizeof(GPixel); + ymap->image(subsample, rect, ptr, rowsep, pixsep); + if (crmap && cbmap && crcb_delay >= 0) + { + cbmap->image(subsample, rect, ptr+1, rowsep, pixsep, crcb_half); + crmap->image(subsample, rect, ptr+2, rowsep, pixsep, crcb_half); + } + // Convert image data to RGB + if (crmap && cbmap && crcb_delay >= 0) + { + Transform::Decode::YCbCr_to_RGB((*ppm)[0], w, h, ppm->rowsize()); + } + else + { + for (int i=0; i<h; i++) + { + GPixel *pixrow = (*ppm)[i]; + for (int j=0; j<w; j++, pixrow++) + pixrow->b = pixrow->g = pixrow->r + = 127 - (int)(((signed char*)pixrow)[0]); + } + } + // Return + return ppm; +} + + +int +IWPixmap::decode_chunk(GP<ByteStream> gbs) +{ + // Open + if (! ycodec) + { + cslice = cserial = 0; + delete ymap; + ymap = 0; + } + + // Read primary header + struct IW44Image::PrimaryHeader primary; + primary.decode(gbs); + if (primary.serial != cserial) + G_THROW( ERR_MSG("IW44Image.wrong_serial2") ); + int nslices = cslice + primary.slices; + // Read secondary header + if (cserial == 0) + { + struct IW44Image::SecondaryHeader secondary; + secondary.decode(gbs); + if ((secondary.major & 0x7f) != IWCODEC_MAJOR) + G_THROW( ERR_MSG("IW44Image.incompat_codec2") ); + if (secondary.minor > IWCODEC_MINOR) + G_THROW( ERR_MSG("IW44Image.recent_codec2") ); + // Read tertiary header + struct IW44Image::TertiaryHeader tertiary; + tertiary.decode(gbs, secondary.major & 0x7f, secondary.minor); + // Handle header information + int w = (tertiary.xhi << 8) | tertiary.xlo; + int h = (tertiary.yhi << 8) | tertiary.ylo; + crcb_delay = 0; + crcb_half = 0; + if (secondary.minor>=2) + crcb_delay = tertiary.crcbdelay & 0x7f; + if (secondary.minor>=2) + crcb_half = (tertiary.crcbdelay & 0x80 ? 0 : 1); + if (secondary.major & 0x80) + crcb_delay = -1; + // Create ymap and ycodec + assert(! ymap); + assert(! ycodec); + ymap = new Map(w, h); + ycodec = new Codec::Decode(*ymap); + if (crcb_delay >= 0) + { + cbmap = new Map(w, h); + crmap = new Map(w, h); + cbcodec = new Codec::Decode(*cbmap); + crcodec = new Codec::Decode(*crmap); + } + } + // Read data + assert(ymap); + assert(ycodec); + GP<ZPCodec> gzp=ZPCodec::create(gbs, false, true); + ZPCodec &zp=*gzp; + int flag = 1; + while (flag && cslice<nslices) + { + flag = ycodec->code_slice(zp); + if (crcodec && cbcodec && crcb_delay<=cslice) + { + flag |= cbcodec->code_slice(zp); + flag |= crcodec->code_slice(zp); + } + cslice++; + } + // Return + cserial += 1; + return nslices; +} + + +int +IWPixmap::parm_crcbdelay(const int parm) +{ + if (parm >= 0) + crcb_delay = parm; + return crcb_delay; +} + +void +IWPixmap::parm_dbfrac(float frac) +{ + if (frac>0 && frac<=1) + db_frac = frac; + else + G_THROW( ERR_MSG("IW44Image.param_range2") ); +} + +int +IWPixmap::get_serial(void) +{ + return cserial; +} + + +void +IWPixmap::decode_iff(IFFByteStream &iff, int maxchunks) +{ + if (ycodec) + G_THROW( ERR_MSG("IW44Image.left_open4") ); + GUTF8String chkid; + iff.get_chunk(chkid); + if (chkid!="FORM:PM44" && chkid!="FORM:BM44") + G_THROW( ERR_MSG("IW44Image.corrupt_BM44_2") ); + while (--maxchunks>=0 && iff.get_chunk(chkid)) + { + if (chkid=="PM44" || chkid=="BM44") + decode_chunk(iff.get_bytestream()); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +////////////////////////////////////////////////////// +// NEW FILTERS +////////////////////////////////////////////////////// + +void +IW44Image::Transform::filter_begin(int w, int h) +{ + if (MMXControl::mmxflag < 0) + MMXControl::enable_mmx(); +} + + +void +IW44Image::Transform::filter_end(void) +{ +#ifdef MMX + if (MMXControl::mmxflag > 0) + MMXemms; +#endif +} + + +////////////////////////////////////////////////////// +// WAVELET TRANSFORM +////////////////////////////////////////////////////// + + +//---------------------------------------------------- +// Function for applying bidimensional IW44 between +// scale intervals begin(inclusive) and end(exclusive) + +void +IW44Image::Transform::Decode::backward(short *p, int w, int h, int rowsize, int begin, int end) +{ + // PREPARATION + filter_begin(w,h); + // LOOP ON SCALES + for (int scale=begin>>1; scale>=end; scale>>=1) + { +#ifdef IWTRANSFORM_TIMER + int tv,th; + th = tv = GOS::ticks(); +#endif + filter_bv(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks(); + tv = th - tv; +#endif + filter_bh(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks()-th; + DjVuPrintErrorUTF8("back%d\tv=%dms h=%dms\n", scale,tv,th); +#endif + } + // TERMINATE + filter_end(); +} + + + + +////////////////////////////////////////////////////// +// COLOR TRANSFORM +////////////////////////////////////////////////////// + +/* Converts YCbCr to RGB. */ +void +IW44Image::Transform::Decode::YCbCr_to_RGB(GPixel *p, int w, int h, int rowsize) +{ + for (int i=0; i<h; i++,p+=rowsize) + { + GPixel *q = p; + for (int j=0; j<w; j++,q++) + { + signed char y = ((signed char*)q)[0]; + signed char b = ((signed char*)q)[1]; + signed char r = ((signed char*)q)[2]; + // This is the Pigeon transform + int t1 = b >> 2 ; + int t2 = r + (r >> 1); + int t3 = y + 128 - t1; + int tr = y + 128 + t2; + int tg = t3 - (t2 >> 1); + int tb = t3 + (b << 1); + q->r = max(0,min(255,tr)); + q->g = max(0,min(255,tg)); + q->b = max(0,min(255,tb)); + } + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/IW44Image.h b/kviewshell/plugins/djvu/libdjvu/IW44Image.h new file mode 100644 index 00000000..56cf00fa --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44Image.h @@ -0,0 +1,761 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: IW44Image.h,v 1.11 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef IW44IMAGE_H_ +#define IW44IMAGE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name IW44Image.h + + Files #"IW44Image.h"# and #"IW44Image.cpp"# implement the DjVu IW44 wavelet + scheme for the compression of gray-level images (see class \Ref{IWBitmap}) + and color images (see class \Ref{IWPixmap}). Programs \Ref{c44} and + \Ref{d44} demonstrate how to encode and decode IW44 files. + + {\bf IW44 File Structure} --- The IW44 files are structured according to + the EA IFF85 specifications (see \Ref{IFFByteStream.h}). Gray level IW44 + Images consist of a single #"FORM:BM44"# chunk composed of an arbitrary + number of #"BM44"# data chunks. Color IW44 Images consist of a single + #"FORM:PM44"# chunk composed of an arbitrary number of #"PM44"# data + chunks. The successive #"PM44"# or #"BM44"# data chunks contain + successive refinements of the encoded image. Each chunk contains a + certain number of ``data slices''. The first chunk also contains a small + image header. You can use program \Ref{djvuinfo} to display all this + structural information: + \begin{verbatim} + % djvuinfo lag.iw4 + lag.iw4: + FORM:PM44 [62598] + PM44 [10807] #1 - 74 slices - v1.2 (color) - 684x510 + PM44 [23583] #2 - 13 slices + PM44 [28178] #3 - 10 slices + \end{verbatim} + + {\bf Embedded IW44 Images} --- These IW44 data chunks can also appear within + other contexts. Files representing a DjVu page, for instance, consist of + a single #"FORM:DJVU"# composite chunk. This composite chunk may contain + #"BG44"# chunks encoding the background layer and #"FG44"# chunks encoding + the foreground color layer. These #"BG44"# and #"FG44"# chunks are + actually regular IW44 data chunks with a different chunk identifier. This + information too can be displayed using program \Ref{djvuinfo}. + \begin{verbatim} + % djvuinfo graham1.djvu + graham1.djvu: + FORM:DJVU [32553] + INFO [5] 3156x2325, version 17 + Sjbz [17692] + BG44 [2570] #1 - 74 slices - v1.2 (color) - 1052x775 + FG44 [1035] #1 - 100 slices - v1.2 (color) - 263x194 + BG44 [3048] #2 - 10 slices + BG44 [894] #3 - 4 slices + BG44 [7247] #4 - 9 slices + \end{verbatim} + + {\bf Performance} --- The main design objective for the DjVu wavelets + consisted of allowing progressive rendering and smooth scrolling of large + images with limited memory requirements. Decoding functions process the + compressed data and update a memory efficient representation of the + wavelet coefficients. Imaging function then can quickly render an + arbitrary segment of the image using the available data. Both process can + be carried out in two threads of execution. This design plays an + important role in the DjVu system. We have investigated various + state-of-the-art wavelet compression schemes: although these schemes may + achieve slightly smaller file sizes, the decoding functions did not even + approach our requirements. + + The IW44 wavelets satisfy these requirements today. It performs very well + for quality settings resulting in high compression ratios. It should not + be used for quasi-lossless compression because certain design choices + deliberately sacrifice the IW44 quasi-lossless performance in order to + improve the image quality at high compression ratios. + + Little care however has been taken to make the IW44 encoder memory + efficient. This code uses two copies of the wavelet coefficient data + structure (one for the raw coefficients, one for the quantized + coefficients). A more sophisticated implementation should considerably + reduce the memory requirements. + + {\bf Masking} --- When we create a DjVu image, we often know that certain + pixels of the background image are going to be covered by foreground + objects like text or drawings. The DjVu IW44 wavelet decomposition + routine can use an optional bilevel image named the mask. Every non zero + pixel in the mask means the value of the corresponding pixel in the + background image is irrelevant. The wavelet decomposition code will + replace these masked pixels by a color value whose coding cost is minimal + (see \URL{http://www.research.att.com/~leonb/DJVU/mask}). + + {\bf ToDo} --- There are many improvements to be made. Besides better + quantization algorithms (such as trellis quantization and bitrate + allocation), we should allow for more wavelet transforms. These + improvements may be implemented in future version, if (and only if) they + can meet our decoding constraints. Future versions will probably split + file #"IW44Image.cpp"# which currently contains everything. + + @memo + Wavelet encoded images. + @author + L\'eon Bottou <[email protected]> + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + + @version + #$Id: IW44Image.h,v 1.11 2003/11/07 22:08:22 leonb Exp $# */ +//@{ + + +#include "GSmartPointer.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class GRect; +class IFFByteStream; +class ByteStream; +class GBitmap; +class GPixmap; + + + +/** IW44 encoding parameters. + This data structure gathers the quality specification parameters needed + for encoding each chunk of an IW44 file. Chunk data is generated until + meeting either the slice target, the size target or the decibel target. */ + +struct IWEncoderParms +{ + /** Slice target. Data generation for the current chunk stops if the total + number of slices (in this chunk and all the previous chunks) reaches + value #slice#. The default value #0# has a special meaning: data will + be generated regardless of the number of slices in the file. */ + int slices; + /** Size target. Data generation for the current chunk stops if the total + data size (in this chunk and all the previous chunks), expressed in + bytes, reaches value #size#. The default value #0# has a special + meaning: data will be generated regardless of the file size. */ + int bytes; + /** Decibel target. Data generation for the current chunk stops if the + estimated luminance error, expressed in decibels, reaches value + #decibel#. The default value #0# has a special meaning: data will be + generated regardless of the estimated luminance error. Specifying value + #0# in fact shortcuts the computation of the estimated luminance error + and sensibly speeds up the encoding process. */ + float decibels; + /** Constructor. Initializes the structure with the default values. */ + IWEncoderParms(void); +}; + + + +/** IW44 encoded gray-level and color images. This class acts as a base for + images represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_bitmap} renders an arbitrary segment of the image into + a \Ref{GBitmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IW44Image : public GPEnabled +{ +public: + /** Chrominance processing selector. The following constants may be used as + argument to the following \Ref{IWPixmap} constructor to indicate how the + chrominance information should be processed. There are four possible values: + \begin{description} + \item[CRCBnone:] The wavelet transform will discard the chrominance + information and only keep the luminance. The image will show in shades of gray. + \item[CRCBhalf:] The wavelet transform will process the chrominance at only + half the image resolution. This option creates smaller files but may create + artifacts in highly colored images. + \item[CRCBnormal:] The wavelet transform will process the chrominance at full + resolution. This is the default. + \item[CRCBfull:] The wavelet transform will process the chrominance at full + resolution. This option also disables the chrominance encoding delay + (see \Ref{parm_crcbdelay}) which usually reduces the bitrate associated with the + chrominance information. + \end{description} */ + enum CRCBMode { + CRCBnone, + CRCBhalf, + CRCBnormal, + CRCBfull }; + class Transform; + class Map; + class Block; + class Codec; + struct Alloc; + struct PrimaryHeader; + struct SecondaryHeader; + struct TertiaryHeader; + enum ImageType { + GRAY=false, + COLOR=true }; +protected: + IW44Image(void); +public: + /** Null constructor. Constructs an empty IW44Image object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. You may not use \Ref{encode_iff} or + \Ref{encode_chunk}. */ + static GP<IW44Image> create_decode(const ImageType itype=COLOR); + /** Null constructor. Constructs an empty IW44Image object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. You may then use \Ref{encode_iff} + and \Ref{encode_chunk}. */ + static GP<IW44Image> create_encode(const ImageType itype=COLOR); + // virtual destructor + virtual ~IW44Image(); + /** Initializes an IWBitmap with image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). */ + static GP<IW44Image> create_encode(const GBitmap &bm, const GP<GBitmap> mask=0); + /** Initializes an IWPixmap with color image #bm#. This constructor + performs the wavelet decomposition of image #bm# and records the + corresponding wavelet coefficient. Argument #mask# is an optional + bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). + Argument #crcbmode# specifies how the chrominance information should be + encoded (see \Ref{CRCBMode}). */ + static GP<IW44Image> create_encode(const GPixmap &bm, const GP<GBitmap> mask=0, CRCBMode crcbmode=CRCBnormal); + // ACCESS + /** Returns the width of the IWBitmap image. */ + int get_width(void) const; + /** Returns the height of the IWBitmap image. */ + int get_height(void) const; + /** Reconstructs the complete image. The reconstructed image + is then returned as a GBitmap object. */ + virtual GP<GBitmap> get_bitmap(void) {return 0;} + /** Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GBitmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GBitmap> get_bitmap(int subsample, const GRect &rect) {return 0;} + /** Reconstructs the complete image. The reconstructed image + is then returned as a GPixmap object. */ + virtual GP<GPixmap> get_pixmap(void) {return 0;} + /** Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GPixmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GPixmap> get_pixmap(int subsample, const GRect &rect) {return 0;} + /** Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const = 0; + /** Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const = 0; + // CODER + /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls + how much data is generated. The chunk data is written to ByteStream + #bs# with no IFF header. Successive calls to #encode_chunk# encode + successive chunks. You must call #close_codec# after encoding the last + chunk of a file. */ + virtual int encode_chunk(GP<ByteStream> gbs, const IWEncoderParms &parms); + /** Writes a gray level image into DjVu IW44 file. This function creates a + composite chunk (identifier #FORM:BM44# or #FORM:PM44#) composed of + #nchunks# chunks (identifier #BM44# or #PM44#). Data for each chunk is + generated with #encode_chunk# using the corresponding parameters in + array #parms#. */ + virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); + // DECODER + /** Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs) = 0; + /** This function enters a composite chunk (identifier #FORM:BM44#, or + #FORM:PM44#), and decodes a maximum of #maxchunks# data chunks + (identifier #BM44#). Data for each chunk is processed using the + function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999) = 0; + // MISCELLANEOUS + /** Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void) = 0; + /** Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void) = 0; + /** Sets the chrominance delay parameter. This function can be called + before encoding the first color IW44 data chunk. Parameter #parm# is an + encoding delay which reduces the bitrate associated with the + chrominance information. The default chrominance encoding delay is 10. */ + virtual int parm_crcbdelay(const int parm) {return parm;} + /** Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac) = 0; +protected: + // Parameter + float db_frac; + // Data + Map *ymap, *cbmap, *crmap; + int cslice; + int cserial; + int cbytes; +private: + // Disable assignment semantic + IW44Image(const IW44Image &ref); + IW44Image& operator=(const IW44Image &ref); +}; + +#ifdef IW44IMAGE_IMPLIMENTATION + +/*x IW44 encoded gray-level image. This class provided functions for managing + a gray level image represented as a collection of IW44 wavelet + coefficients. The coefficients are stored in a memory efficient data + structure. Member function \Ref{get_bitmap} renders an arbitrary segment + of the image into a \Ref{GBitmap}. Member functions \Ref{decode_iff} and + \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). + Both the copy constructor and the copy operator are declared as private + members. It is therefore not possible to make multiple copies of instances + of this class. */ + +class IWBitmap : public IW44Image +{ +public: + friend class IW44Image; + class Encode; +protected: + /*x Null constructor. Constructs an empty IWBitmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + IWBitmap(void); +public: + //x virtual destructor + virtual ~IWBitmap(); + //x ACCESS + /*x Reconstructs the complete image. The reconstructed image + is then returned as a GBitmap object. */ + virtual GP<GBitmap> get_bitmap(void); + /*x Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GBitmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GBitmap> get_bitmap(int subsample, const GRect &rect); + /*x Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const; + /*x Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const; + // DECODER + /*x Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs); + /*x Reads a DjVu IW44 file as a gray level image. This function enters a + composite chunk (identifier #FORM:BM44#), and decodes a maximum of + #maxchunks# data chunks (identifier #BM44#). Data for each chunk is + processed using the function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999); + // MISCELLANEOUS + /*x Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); + /*x Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void); + /*x Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac); +private: + Codec *ycodec; +}; + + +/*x IW44 encoded color image. This class provided functions for managing a + color image represented as a collection of IW44 wavelet coefficients. The + coefficients are stored in a memory efficient data structure. Member + function \Ref{get_pixmap} renders an arbitrary segment of the image into a + \Ref{GPixmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} + read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy + constructor and the copy operator are declared as private members. It is + therefore not possible to make multiple copies of instances of this + class. */ + +class IWPixmap : public IW44Image +{ +public: + friend class IW44Image; +protected: + class Encode; + /*x Null constructor. Constructs an empty IWPixmap object. This object does + not contain anything meaningful. You must call function \Ref{init}, + \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet + coefficient data structure. */ + IWPixmap(void); +public: + // virtual destructor + virtual ~IWPixmap(); + // ACCESS + /*x Reconstructs the complete image. The reconstructed image + is then returned as a GPixmap object. */ + virtual GP<GPixmap> get_pixmap(void); + /*x Reconstructs a segment of the image at a given scale. The subsampling + ratio #subsample# must be a power of two between #1# and #32#. Argument + #rect# specifies which segment of the subsampled image should be + reconstructed. The reconstructed image is returned as a GPixmap object + whose size is equal to the size of the rectangle #rect#. */ + virtual GP<GPixmap> get_pixmap(int subsample, const GRect &rect); + /*x Returns the amount of memory used by the wavelet coefficients. This + amount of memory is expressed in bytes. */ + virtual unsigned int get_memory_usage(void) const; + /*x Returns the filling ratio of the internal data structure. Wavelet + coefficients are stored in a sparse array. This function tells what + percentage of bins have been effectively allocated. */ + virtual int get_percent_memory(void) const; + // DECODER + /*x Decodes one data chunk from ByteStream #bs#. Successive calls to + #decode_chunk# decode successive chunks. You must call #close_codec# + after decoding the last chunk of a file. Note that function + #get_bitmap# and #decode_chunk# may be called simultaneously from two + execution threads. */ + virtual int decode_chunk(GP<ByteStream> gbs); + /*x Reads a DjVu IW44 file as a color image. This function enters a + composite chunk (identifier #FORM:PM44# or #FORM:BM44#), and decodes a + maximum of #maxchunks# data chunks (identifier #PM44# or #BM44#). Data + for each chunk is processed using the function #decode_chunk#. */ + virtual void decode_iff(IFFByteStream &iff, int maxchunks=999); + // MISCELLANEOUS + /*x Resets the encoder/decoder state. The first call to #decode_chunk# or + #encode_chunk# initializes the coder for encoding or decoding. Function + #close_codec# must be called after processing the last chunk in order to + reset the coder and release the associated memory. */ + virtual void close_codec(void); + /*x Returns the chunk serial number. This function returns the serial + number of the last chunk encoded with #encode_chunk# or decoded with + #decode_chunk#. The first chunk always has serial number #1#. Successive + chunks have increasing serial numbers. Value #0# is returned if this + function is called before calling #encode_chunk# or #decode_chunk# or + after calling #close_codec#. */ + virtual int get_serial(void); + /*x Sets the chrominance delay parameter. This function can be called + before encoding the first IW44 data chunk. Parameter #parm# is an + encoding delay which reduces the bitrate associated with the + chrominance information. The default chrominance encoding delay is 10. */ + virtual int parm_crcbdelay(const int parm); + /*x Sets the #dbfrac# parameter. This function can be called before + encoding the first IW44 data chunk. Parameter #frac# modifies the + decibel estimation algorithm in such a way that the decibel target only + pertains to the average error of the fraction #frac# of the most + misrepresented 32x32 pixel blocks. Setting arguments #frac# to #1.0# + restores the normal behavior. */ + virtual void parm_dbfrac(float frac); +protected: + // Parameter + int crcb_delay; + int crcb_half; + // Data +private: + Codec *ycodec, *cbcodec, *crcodec; +}; + +/*x IW44Transform. +*/ +class IW44Image::Transform +{ +public: + class Decode; + class Encode; +protected: + static void filter_begin(int w, int h); + static void filter_end(void); +}; + +struct GPixel; +class IW44Image::Transform::Decode : public IW44Image::Transform +{ +public: + // WAVELET TRANSFORM + /*x Forward transform. */ + static void backward(short *p, int w, int h, int rowsize, int begin, int end); + + // COLOR TRANSFORM + /*x Converts YCbCr to RGB. */ + static void YCbCr_to_RGB(GPixel *p, int w, int h, int rowsize); +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Block [declaration] +// Represents a block of 32x32 coefficients after zigzagging and scaling + + +class IW44Image::Block // DJVU_CLASS +{ +public: + // creating + Block(void); + // accessing scaled coefficients + short get(int n) const; + void set(int n, int val, IW44Image::Map *map); + // converting from liftblock + void read_liftblock(const short *coeff, IW44Image::Map *map); + void write_liftblock(short *coeff, int bmin=0, int bmax=64) const; + // sparse array access + const short* data(int n) const; + short* data(int n, IW44Image::Map *map); + void zero(int n); + // sparse representation +private: + short **(pdata[4]); +}; + +//--------------------------------------------------------------- +// *** Class IW44Image::Map [declaration] +// Represents all the blocks of an image + +class IW44Image::Map // DJVU_CLASS +{ +public: + class Encode; + + // construction + Map(int w, int h); + ~Map(); + // image access + void image(signed char *img8, int rowsize, + int pixsep=1, int fast=0); + void image(int subsample, const GRect &rect, + signed char *img8, int rowsize, + int pixsep=1, int fast=0); + // array of blocks + IW44Image::Block *blocks; + // geometry + int iw, ih; + int bw, bh; + int nb; + // coefficient allocation stuff + short *alloc(int n); + short **allocp(int n); + IW44Image::Alloc *chain; + int top; + // statistics + int get_bucket_count(void) const; + unsigned int get_memory_usage(void) const; +}; + +////////////////////////////////////////////////////// +// ENCODING/DECODING WAVELET COEFFICIENTS +// USING HIERARCHICAL SET DIFFERENCE +////////////////////////////////////////////////////// + + +//----------------------------------------------- +// Class IW44Image::Codec [declaration+implementation] +// Maintains information shared while encoding or decoding + +class IW44Image::Codec +{ +public: + class Decode; + class Encode; + +protected: + // Construction + Codec(IW44Image::Map &map); +public: + virtual ~Codec(); + // Coding + int finish_code_slice(ZPCodec &zp); + virtual int code_slice(ZPCodec &zp) = 0; + // Data + IW44Image::Map ↦ // working map + // status + int curband; // current band + int curbit; // current bitplane + // quantization tables + int quant_hi[10]; // quantization for bands 1 to 9 + int quant_lo[16]; // quantization for band 0. + // bucket state + char coeffstate[256]; + char bucketstate[16]; + enum { ZERO = 1, // this coeff never hits this bit + ACTIVE = 2, // this coeff is already active + NEW = 4, // this coeff is becoming active + UNK = 8 }; // this coeff may become active + // coding context + BitContext ctxStart [32]; + BitContext ctxBucket[10][8]; + BitContext ctxMant; + BitContext ctxRoot; + // helper + int is_null_slice(int bit, int band); + int decode_prepare(int fbucket, int nbucket, IW44Image::Block &blk); + void decode_buckets(ZPCodec &zp, int bit, int band, + IW44Image::Block &blk, int fbucket, int nbucket); +}; + +////////////////////////////////////////////////////// +// DEFINITION OF CHUNK HEADERS +////////////////////////////////////////////////////// + + +struct IW44Image::PrimaryHeader { + unsigned char serial; + unsigned char slices; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs); +}; + +struct IW44Image::SecondaryHeader { + unsigned char major; + unsigned char minor; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs); +}; + +struct IW44Image::TertiaryHeader { + unsigned char xhi, xlo; + unsigned char yhi, ylo; + unsigned char crcbdelay; + void encode(GP<ByteStream> gbs); + void decode(GP<ByteStream> gbs, int major=1, int minor=2); +}; + +inline const short* +IW44Image::Block::data(int n) const +{ + if (! pdata[n>>4]) + return 0; + return pdata[n>>4][n&15]; +} + +inline short* +IW44Image::Block::data(int n, IW44Image::Map *map) +{ + if (! pdata[n>>4]) + pdata[n>>4] = map->allocp(16); + if (! pdata[n>>4][n &15]) + pdata[n>>4][n &15] = map->alloc(16); + return pdata[n>>4][n&15]; +} + +inline short +IW44Image::Block::get(int n) const +{ + int n1 = (n>>4); + const short *d = data(n1); + if (! d) + return 0; + return d[n&15]; +} + +inline void +IW44Image::Block::set(int n, int val, IW44Image::Map *map) +{ + int n1 = (n>>4); + short* d = data(n1, map); + d[n&15] = val; +} + +#endif /* IW44IMAGE_IMPLIMENTATION */ + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp b/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp new file mode 100644 index 00000000..5a8092a0 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp @@ -0,0 +1,564 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2EncodeCodec.cpp,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#ifndef NEED_DECODER_ONLY + +#include "JB2Image.h" +#include "GBitmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////// +//// CLASS JB2Codec::Encode: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the encode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the JB2 coder. +// Contains all contextual information for encoding a JB2Image. + +class JB2Dict::JB2Codec::Encode : public JB2Dict::JB2Codec +{ +public: + Encode(void); + void init(const GP<ByteStream> &gbs); +//virtual + void code(const GP<JB2Image> &jim); + void code(JB2Image *jim) { const GP<JB2Image> gjim(jim);code(gjim); } + void code(const GP<JB2Dict> &jim); + void code(JB2Dict *jim) { const GP<JB2Dict> gjim(jim);code(gjim); } + +protected: + void CodeNum(const int num, const int lo, const int hi, NumContext &ctx); + void encode_libonly_shape(const GP<JB2Image> &jim, int shapeno); +// virtual + bool CodeBit(const bool bit, BitContext &ctx); + void code_comment(GUTF8String &comment); + void code_record_type(int &rectype); + int code_match_index(int &index, JB2Dict &jim); + void code_inherited_shape_count(JB2Dict &jim); + void code_image_size(JB2Dict &jim); + void code_image_size(JB2Image &jim); + void code_absolute_location(JB2Blit *jblt, int rows, int columns); + void code_absolute_mark_size(GBitmap &bm, int border=0); + void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0); + void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ); + int get_diff(const int x_diff,NumContext &rel_loc); + void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ); + +private: + GP<ZPCodec> gzp; +}; + + +//////////////////////////////////////// +//// CLASS JB2DICT: IMPLEMENTATION +//////////////////////////////////////// + +void +JB2Dict::encode(const GP<ByteStream> &gbs) const +{ + JB2Codec::Encode codec; + codec.init(gbs); + codec.code(const_cast<JB2Dict *>(this)); +} + +//////////////////////////////////////// +//// CLASS JB2IMAGE: IMPLEMENTATION +//////////////////////////////////////// + +void +JB2Image::encode(const GP<ByteStream> &gbs) const +{ + JB2Codec::Encode codec; + codec.init(gbs); + codec.code(const_cast<JB2Image *>(this)); +} + +//////////////////////////////////////// +//// CLASS JB2CODEC : IMPLEMENTATION +//////////////////////////////////////// + +#define START_OF_DATA (0) +#define NEW_MARK (1) +#define NEW_MARK_LIBRARY_ONLY (2) +#define NEW_MARK_IMAGE_ONLY (3) +#define MATCHED_REFINE (4) +#define MATCHED_REFINE_LIBRARY_ONLY (5) +#define MATCHED_REFINE_IMAGE_ONLY (6) +#define MATCHED_COPY (7) +#define NON_MARK_DATA (8) +#define REQUIRED_DICT_OR_RESET (9) +#define PRESERVED_COMMENT (10) +#define END_OF_DATA (11) + +// STATIC DATA MEMBERS + +static const int BIGPOSITIVE = 262142; +static const int BIGNEGATIVE = -262143; +static const int CELLCHUNK = 20000; +static const int CELLEXTRA = 500; + +// CONSTRUCTOR + +JB2Dict::JB2Codec::Encode::Encode(void) +: JB2Dict::JB2Codec(1) {} + +void +JB2Dict::JB2Codec::Encode::init(const GP<ByteStream> &gbs) +{ + gzp=ZPCodec::create(gbs,true,true); +} + +inline bool +JB2Dict::JB2Codec::Encode::CodeBit(const bool bit, BitContext &ctx) +{ + gzp->encoder(bit?1:0, ctx); + return bit; +} + +void +JB2Dict::JB2Codec::Encode::CodeNum(int num, int low, int high, NumContext &ctx) +{ + if (num < low || num > high) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + JB2Codec::CodeNum(low,high,&ctx,num); +} + +// CODE COMMENTS + +void +JB2Dict::JB2Codec::Encode::code_comment(GUTF8String &comment) +{ + // Encode size + int size=comment.length(); + CodeNum(size, 0, BIGPOSITIVE, dist_comment_length); + for (int i=0; i<size; i++) + { + CodeNum(comment[i], 0, 255, dist_comment_byte); + } +} + +// CODE SIMPLE VALUES + +inline void +JB2Dict::JB2Codec::Encode::code_record_type(int &rectype) +{ + CodeNum(rectype, START_OF_DATA, END_OF_DATA, dist_record_type); +} + +int +JB2Dict::JB2Codec::Encode::code_match_index(int &index, JB2Dict &jim) +{ + int match=shape2lib[index]; + CodeNum(match, 0, lib2shape.hbound(), dist_match_index); + return match; +} + +// CODE PAIRS + +void +JB2Dict::JB2Codec::Encode::code_inherited_shape_count(JB2Dict &jim) +{ + CodeNum(jim.get_inherited_shape_count(), + 0, BIGPOSITIVE, inherited_shape_count_dist); +} + +void +JB2Dict::JB2Codec::Encode::code_image_size(JB2Dict &jim) +{ + CodeNum(0, 0, BIGPOSITIVE, image_size_dist); + CodeNum(0, 0, BIGPOSITIVE, image_size_dist); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::Encode::code_image_size(JB2Image &jim) +{ + image_columns = jim.get_width(); + CodeNum(image_columns, 0, BIGPOSITIVE, image_size_dist); + image_rows = jim.get_height(); + CodeNum(image_rows, 0, BIGPOSITIVE, image_size_dist); + JB2Codec::code_image_size(jim); +} + +inline int +JB2Dict::JB2Codec::Encode::get_diff(int x_diff,NumContext &rel_loc) +{ + CodeNum(x_diff, BIGNEGATIVE, BIGPOSITIVE, rel_loc); + return x_diff; +} + +void +JB2Dict::JB2Codec::Encode::code_absolute_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + // Code TOP and LEFT + CodeNum(jblt->left+1, 1, image_columns, abs_loc_x); + CodeNum(jblt->bottom+rows-1+1, 1, image_rows, abs_loc_y); +} + +void +JB2Dict::JB2Codec::Encode::code_absolute_mark_size(GBitmap &bm, int border) +{ + CodeNum(bm.columns(), 0, BIGPOSITIVE, abs_size_x); + CodeNum(bm.rows(), 0, BIGPOSITIVE, abs_size_y); +} + +void +JB2Dict::JB2Codec::Encode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border) +{ + CodeNum(bm.columns()-cw, BIGNEGATIVE, BIGPOSITIVE, rel_size_x); + CodeNum(bm.rows()-ch, BIGNEGATIVE, BIGPOSITIVE, rel_size_y); +} + +// CODE BITMAP DIRECTLY + +void +JB2Dict::JB2Codec::Encode::code_bitmap_directly( + GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (encoding) + while (dy >= 0) + { + int context=get_direct_context(up2, up1, up0, 0); + for (int dx=0;dx < dw;) + { + int n = up0[dx++]; + zp.encoder(n, bitdist[context]); + context=shift_direct_context(context, n, up2, up1, up0, dx); + } + // next row + dy -= 1; + up2 = up1; + up1 = up0; + up0 = bm[dy]; + } +} + +// CODE BITMAP BY CROSS CODING + +void +JB2Dict::JB2Codec::Encode::code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (encoding) + while (dy >= 0) + { + int context=get_cross_context(up1, up0, xup1, xup0, xdn1, 0); + for(int dx=0;dx < dw;) + { + const int n = up0[dx++]; + zp.encoder(n, cbitdist[context]); + context=shift_cross_context(context, n, + up1, up0, xup1, xup0, xdn1, dx); + } + // next row + up1 = up0; + up0 = bm[--dy]; + xup1 = xup0; + xup0 = xdn1; + xdn1 = cbm[(--cy)-1] + xd2c; + } +} + +// CODE JB2DICT + +void +JB2Dict::JB2Codec::Encode::code(const GP<JB2Dict> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + // ------------------------- + // THIS IS THE ENCODING PART + // ------------------------- + int firstshape = jim.get_inherited_shape_count(); + int nshape = jim.get_shape_count(); + init_library(jim); + // Code headers. + int rectype = REQUIRED_DICT_OR_RESET; + if (jim.get_inherited_shape_count() > 0) + code_record(rectype, gjim, 0); + rectype = START_OF_DATA; + code_record(rectype, gjim, 0); + // Code Comment. + rectype = PRESERVED_COMMENT; + if (!! jim.comment) + code_record(rectype, gjim, 0); + // Encode every shape + int shapeno; + DJVU_PROGRESS_TASK(jb2code,"jb2 encode", nshape-firstshape); + for (shapeno=firstshape; shapeno<nshape; shapeno++) + { + DJVU_PROGRESS_RUN(jb2code, (shapeno-firstshape)|0xff); + // Code shape + JB2Shape &jshp = jim.get_shape(shapeno); + rectype=(jshp.parent >= 0) + ?MATCHED_REFINE_LIBRARY_ONLY:NEW_MARK_LIBRARY_ONLY; + code_record(rectype, gjim, &jshp); + add_library(shapeno, jshp); + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0); + gzp=0; +} + +// CODE JB2IMAGE + +void +JB2Dict::JB2Codec::Encode::code(const GP<JB2Image> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // ------------------------- + // THIS IS THE ENCODING PART + // ------------------------- + int i; + init_library(jim); + int firstshape = jim.get_inherited_shape_count(); + int nshape = jim.get_shape_count(); + int nblit = jim.get_blit_count(); + // Initialize shape2lib + shape2lib.resize(0,nshape-1); + for (i=firstshape; i<nshape; i++) + shape2lib[i] = -1; + // Determine shapes that go into library (shapeno>=firstshape) + // shape2lib is -2 if used by one blit + // shape2lib is -3 if used by more than one blit + // shape2lib is -4 if used as a parent + for (i=0; i<nblit; i++) + { + JB2Blit *jblt = jim.get_blit(i); + int shapeno = jblt->shapeno; + if (shapeno < firstshape) + continue; + if (shape2lib[shapeno] >= -2) + shape2lib[shapeno] -= 1; + shapeno = jim.get_shape(shapeno).parent; + while (shapeno>=firstshape && shape2lib[shapeno]>=-3) + { + shape2lib[shapeno] = -4; + shapeno = jim.get_shape(shapeno).parent; + } + } + // Code headers. + int rectype = REQUIRED_DICT_OR_RESET; + if (jim.get_inherited_shape_count() > 0) + code_record(rectype, gjim, 0, 0); + rectype = START_OF_DATA; + code_record(rectype, gjim, 0, 0); + // Code Comment. + rectype = PRESERVED_COMMENT; + if (!! jim.comment) + code_record(rectype, gjim, 0, 0); + // Encode every blit + int blitno; + DJVU_PROGRESS_TASK(jb2code,"jb2 encode", nblit); + for (blitno=0; blitno<nblit; blitno++) + { + DJVU_PROGRESS_RUN(jb2code, blitno|0xff); + JB2Blit *jblt = jim.get_blit(blitno); + int shapeno = jblt->shapeno; + JB2Shape &jshp = jim.get_shape(shapeno); + // Tests if shape exists in library + if (shape2lib[shapeno] >= 0) + { + int rectype = MATCHED_COPY; + code_record(rectype, gjim, 0, jblt); + } + // Avoid coding null shapes/blits + else if (jshp.bits) + { + // Make sure all parents have been coded + if (jshp.parent>=0 && shape2lib[jshp.parent]<0) + encode_libonly_shape(gjim, jshp.parent); + // Allocate library entry when needed +#define LIBRARY_CONTAINS_ALL + int libraryp = 0; +#ifdef LIBRARY_CONTAINS_MARKS // baseline + if (jshp.parent >= -1) + libraryp = 1; +#endif +#ifdef LIBRARY_CONTAINS_SHARED // worse + if (shape2lib[shapeno] <= -3) + libraryp = 1; +#endif +#ifdef LIBRARY_CONTAINS_ALL // better + libraryp = 1; +#endif + // Test all blit cases + if (jshp.parent<-1 && !libraryp) + { + int rectype = NON_MARK_DATA; + code_record(rectype, gjim, &jshp, jblt); + } + else if (jshp.parent < 0) + { + int rectype = (libraryp ? NEW_MARK : NEW_MARK_IMAGE_ONLY); + code_record(rectype, gjim, &jshp, jblt); + } + else + { + int rectype = (libraryp ? MATCHED_REFINE : MATCHED_REFINE_IMAGE_ONLY); + code_record(rectype, gjim, &jshp, jblt); + } + // Add shape to library + if (libraryp) + add_library(shapeno, jshp); + } + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0, 0); + gzp=0; +} + +//////////////////////////////////////// +//// HELPERS +//////////////////////////////////////// + +void +JB2Dict::JB2Codec::Encode::encode_libonly_shape( + const GP<JB2Image> &gjim, int shapeno ) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // Recursively encode parent shape + JB2Shape &jshp = jim.get_shape(shapeno); + if (jshp.parent>=0 && shape2lib[jshp.parent]<0) + encode_libonly_shape(gjim, jshp.parent); + // Test that library shape must be encoded + if (shape2lib[shapeno] < 0) + { + // Code library entry + int rectype=(jshp.parent >= 0) + ?NEW_MARK_LIBRARY_ONLY:MATCHED_REFINE_LIBRARY_ONLY; + code_record(rectype, gjim, &jshp, 0); + // Add shape to library + add_library(shapeno, jshp); + // Check numcoder status + if (cur_ncell > CELLCHUNK) + { + rectype = REQUIRED_DICT_OR_RESET; + code_record(rectype, 0, 0); + } + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif /* NEED_DECODER_ONLY */ + diff --git a/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp b/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp new file mode 100644 index 00000000..7aad9261 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp @@ -0,0 +1,1427 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2Image.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +#include "JB2Image.h" +#include "GThreads.h" +#include "GRect.h" +#include "GBitmap.h" +#include <string.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////// +//// CLASS JB2Codec::Decode: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the decode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the JB2 decoder. +// Contains all contextual information for decoding a JB2Image. + +class JB2Dict::JB2Codec::Decode : public JB2Dict::JB2Codec +{ +public: + Decode(void); + void init(const GP<ByteStream> &gbs); +// virtual + void code(const GP<JB2Image> &jim); + void code(JB2Image *jim) {const GP<JB2Image> gjim(jim);code(gjim);} + void code(const GP<JB2Dict> &jim); + void code(JB2Dict *jim) {const GP<JB2Dict> gjim(jim);code(gjim);} + void set_dict_callback(JB2DecoderCallback *cb, void *arg); +protected: + int CodeNum(const int lo, const int hi, NumContext &ctx); + +// virtual + bool CodeBit(const bool bit, BitContext &ctx); + void code_comment(GUTF8String &comment); + void code_record_type(int &rectype); + int code_match_index(int &index, JB2Dict &jim); + void code_inherited_shape_count(JB2Dict &jim); + void code_image_size(JB2Dict &jim); + void code_image_size(JB2Image &jim); + void code_absolute_location(JB2Blit *jblt, int rows, int columns); + void code_absolute_mark_size(GBitmap &bm, int border=0); + void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0); + void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ); + void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ); + int get_diff(const int x_diff,NumContext &rel_loc); + +private: + GP<ZPCodec> gzp; + JB2DecoderCallback *cbfunc; + void *cbarg; +}; + +//////////////////////////////////////// +//// CLASS JB2DICT: IMPLEMENTATION +//////////////////////////////////////// + + +JB2Dict::JB2Dict() + : inherited_shapes(0) +{ +} + +void +JB2Dict::init() +{ + inherited_shapes = 0; + inherited_dict = 0; + shapes.empty(); +} + +JB2Shape & +JB2Dict::get_shape(const int shapeno) +{ + JB2Shape *retval; + if(shapeno >= inherited_shapes) + { + retval=&shapes[shapeno - inherited_shapes]; + }else if(inherited_dict) + { + retval=&(inherited_dict->get_shape(shapeno)); + }else + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + return *retval; +} + +const JB2Shape & +JB2Dict::get_shape(const int shapeno) const +{ + const JB2Shape *retval; + if(shapeno >= inherited_shapes) + { + retval=&shapes[shapeno - inherited_shapes]; + }else if(inherited_dict) + { + retval=&(inherited_dict->get_shape(shapeno)); + }else + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + return *retval; +} + +void +JB2Dict::set_inherited_dict(const GP<JB2Dict> &dict) +{ + if (shapes.size() > 0) + G_THROW( ERR_MSG("JB2Image.cant_set") ); + if (inherited_dict) + G_THROW( ERR_MSG("JB2Image.cant_change") ); + inherited_dict = dict; + inherited_shapes = dict->get_shape_count(); + // Make sure that inherited bitmaps are marked as shared + for (int i=0; i<inherited_shapes; i++) + { + JB2Shape &jshp = dict->get_shape(i); + if (jshp.bits) jshp.bits->share(); + } +} + +void +JB2Dict::compress() +{ + for (int i=shapes.lbound(); i<=shapes.hbound(); i++) + shapes[i].bits->compress(); +} + +unsigned int +JB2Dict::get_memory_usage() const +{ + unsigned int usage = sizeof(JB2Dict); + usage += sizeof(JB2Shape) * shapes.size(); + for (int i=shapes.lbound(); i<=shapes.hbound(); i++) + if (shapes[i].bits) + usage += shapes[i].bits->get_memory_usage(); + return usage; +} + +int +JB2Dict::add_shape(const JB2Shape &shape) +{ + if (shape.parent >= get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_parent_shape") ); + int index = shapes.size(); + shapes.touch(index); + shapes[index] = shape; + return index + inherited_shapes; +} + +void +JB2Dict::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg) +{ + init(); + JB2Codec::Decode codec; + codec.init(gbs); + codec.set_dict_callback(cb,arg); + codec.code(this); +} + + + +//////////////////////////////////////// +//// CLASS JB2IMAGE: IMPLEMENTATION +//////////////////////////////////////// + + +JB2Image::JB2Image(void) + : width(0), height(0), reproduce_old_bug(false) +{ +} + +void +JB2Image::init(void) +{ + width = height = 0; + blits.empty(); + JB2Dict::init(); +} + +unsigned int +JB2Image::get_memory_usage() const +{ + unsigned int usage = JB2Dict::get_memory_usage(); + usage += sizeof(JB2Image) - sizeof(JB2Dict); + usage += sizeof(JB2Blit) * blits.size(); + return usage; +} + +void +JB2Image::set_dimension(int awidth, int aheight) +{ + width = awidth; + height = aheight; +} + +int +JB2Image::add_blit(const JB2Blit &blit) +{ + if (blit.shapeno >= (unsigned int)get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_shape") ); + int index = blits.size(); + blits.touch(index); + blits[index] = blit; + return index; +} + +GP<GBitmap> +JB2Image::get_bitmap(int subsample, int align) const +{ + if (width==0 || height==0) + G_THROW( ERR_MSG("JB2Image.cant_create") ); + int swidth = (width + subsample - 1) / subsample; + int sheight = (height + subsample - 1) / subsample; + int border = ((swidth + align - 1) & ~(align - 1)) - swidth; + GP<GBitmap> bm = GBitmap::create(sheight, swidth, border); + bm->set_grays(1+subsample*subsample); + for (int blitno = 0; blitno < get_blit_count(); blitno++) + { + const JB2Blit *pblit = get_blit(blitno); + const JB2Shape &pshape = get_shape(pblit->shapeno); + if (pshape.bits) + bm->blit(pshape.bits, pblit->left, pblit->bottom, subsample); + } + return bm; +} + +GP<GBitmap> +JB2Image::get_bitmap(const GRect &rect, int subsample, int align, int dispy) const +{ + if (width==0 || height==0) + G_THROW( ERR_MSG("JB2Image.cant_create") ); + int rxmin = rect.xmin * subsample; + int rymin = rect.ymin * subsample; + int swidth = rect.width(); + int sheight = rect.height(); + int border = ((swidth + align - 1) & ~(align - 1)) - swidth; + GP<GBitmap> bm = GBitmap::create(sheight, swidth, border); + bm->set_grays(1+subsample*subsample); + for (int blitno = 0; blitno < get_blit_count(); blitno++) + { + const JB2Blit *pblit = get_blit(blitno); + const JB2Shape &pshape = get_shape(pblit->shapeno); + if (pshape.bits) + bm->blit(pshape.bits, pblit->left-rxmin, pblit->bottom-rymin+dispy, subsample); + } + return bm; +} + +void +JB2Image::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg) +{ + init(); + JB2Codec::Decode codec; + codec.init(gbs); + codec.set_dict_callback(cb,arg); + codec.code(this); +} + + + +//////////////////////////////////////// +//// CLASS JB2CODEC : IMPLEMENTATION +//////////////////////////////////////// + + + +#define START_OF_DATA (0) +#define NEW_MARK (1) +#define NEW_MARK_LIBRARY_ONLY (2) +#define NEW_MARK_IMAGE_ONLY (3) +#define MATCHED_REFINE (4) +#define MATCHED_REFINE_LIBRARY_ONLY (5) +#define MATCHED_REFINE_IMAGE_ONLY (6) +#define MATCHED_COPY (7) +#define NON_MARK_DATA (8) +#define REQUIRED_DICT_OR_RESET (9) +#define PRESERVED_COMMENT (10) +#define END_OF_DATA (11) + + + +// STATIC DATA MEMBERS + +static const int BIGPOSITIVE = 262142; +static const int BIGNEGATIVE = -262143; +static const int CELLCHUNK = 20000; +static const int CELLEXTRA = 500; + + +// CONSTRUCTOR + +JB2Dict::JB2Codec::Decode::Decode(void) +: JB2Dict::JB2Codec(0), cbfunc(0), cbarg(0) {} + +void +JB2Dict::JB2Codec::Decode::init(const GP<ByteStream> &gbs) +{ + gzp=ZPCodec::create(gbs,false,true); +} + +JB2Dict::JB2Codec::JB2Codec(const bool xencoding) + : encoding(xencoding), + cur_ncell(0), + gbitcells(bitcells,CELLCHUNK+CELLEXTRA), + gleftcell(leftcell,CELLCHUNK+CELLEXTRA), + grightcell(rightcell,CELLCHUNK+CELLEXTRA), + refinementp(false), + gotstartrecordp(0), + dist_comment_byte(0), + dist_comment_length(0), + dist_record_type(0), + dist_match_index(0), + dist_refinement_flag(0), + abs_loc_x(0), + abs_loc_y(0), + abs_size_x(0), + abs_size_y(0), + image_size_dist(0), + inherited_shape_count_dist(0), + offset_type_dist(0), + rel_loc_x_current(0), + rel_loc_x_last(0), + rel_loc_y_current(0), + rel_loc_y_last(0), + rel_size_x(0), + rel_size_y(0) +{ + memset(bitdist, 0, sizeof(bitdist)); + memset(cbitdist, 0, sizeof(cbitdist)); + // Initialize numcoder + bitcells[0] = 0; // dummy cell + leftcell[0] = rightcell[0] = 0; + cur_ncell = 1; +} + +JB2Dict::JB2Codec::~JB2Codec() {} + +void +JB2Dict::JB2Codec::reset_numcoder() +{ + dist_comment_byte = 0; + dist_comment_length = 0; + dist_record_type = 0; + dist_match_index = 0; + abs_loc_x = 0; + abs_loc_y = 0; + abs_size_x = 0; + abs_size_y = 0; + image_size_dist = 0; + inherited_shape_count_dist = 0; + rel_loc_x_current = 0; + rel_loc_x_last = 0; + rel_loc_y_current = 0; + rel_loc_y_last = 0; + rel_size_x = 0; + rel_size_y = 0; + gbitcells.clear(); + gleftcell.clear(); + grightcell.clear(); + cur_ncell = 1; +} + + +void +JB2Dict::JB2Codec::Decode::set_dict_callback(JB2DecoderCallback *cb, void *arg) +{ + cbfunc = cb; + cbarg = arg; +} + + +// CODE NUMBERS + +inline bool +JB2Dict::JB2Codec::Decode::CodeBit(const bool, BitContext &ctx) +{ + return gzp->decoder(ctx)?true:false; +} + +int +JB2Dict::JB2Codec::Decode::CodeNum(int low, int high, NumContext &ctx) +{ + return JB2Codec::CodeNum(low,high,&ctx,0); +} + +int +JB2Dict::JB2Codec::CodeNum(int low, int high, NumContext *pctx, int v) +{ + bool negative=false; + int cutoff; + // Check + if (!pctx || ((int)*pctx >= cur_ncell)) + G_THROW( ERR_MSG("JB2Image.bad_numcontext") ); + // Start all phases + cutoff = 0; + for(int phase=1,range=0xffffffff;range != 1;) + { + if (! *pctx) + { + const int max_ncell=gbitcells; + if (cur_ncell >= max_ncell) + { + const int nmax_ncell = max_ncell+CELLCHUNK; + gbitcells.resize(nmax_ncell); + gleftcell.resize(nmax_ncell); + grightcell.resize(nmax_ncell); + } + *pctx = cur_ncell ++; + bitcells[*pctx] = 0; + leftcell[*pctx] = rightcell[*pctx] = 0; + } + // encode + const bool decision = encoding + ? ((low < cutoff && high >= cutoff) + ? CodeBit((v>=cutoff),bitcells[*pctx]) + : (v >= cutoff)) + : ((low>=cutoff)||((high>=cutoff)&&CodeBit(false,bitcells[*pctx]))); + // context for new bit + pctx = decision?(&rightcell[*pctx]):(&leftcell[*pctx]); + // phase dependent part + switch (phase) + { + case 1: + negative = !decision; + if (negative) + { + if (encoding) + v = - v - 1; + const int temp = - low - 1; + low = - high - 1; + high = temp; + } + phase = 2; cutoff = 1; + break; + + case 2: + if (!decision) + { + phase = 3; + range = (cutoff + 1) / 2; + if (range == 1) + cutoff = 0; + else + cutoff -= range / 2; + } + else + { + cutoff += cutoff + 1; + } + break; + + case 3: + range /= 2; + if (range != 1) + { + if (!decision) + cutoff -= range / 2; + else + cutoff += range / 2; + } + else if (!decision) + { + cutoff --; + } + break; + } + } + return (negative)?(- cutoff - 1):cutoff; +} + + + +// CODE COMMENTS + +void +JB2Dict::JB2Codec::Decode::code_comment(GUTF8String &comment) +{ + int size=CodeNum(0, BIGPOSITIVE, dist_comment_length); + comment.empty(); + char *combuf = comment.getbuf(size); + for (int i=0; i<size; i++) + { + combuf[i]=CodeNum(0, 255, dist_comment_byte); + } + comment.getbuf(); +} + + +// LIBRARY + + +void +JB2Dict::JB2Codec::init_library(JB2Dict &jim) +{ + int nshape = jim.get_inherited_shape_count(); + shape2lib.resize(0,nshape-1); + lib2shape.resize(0,nshape-1); + libinfo.resize(0,nshape-1); + for (int i=0; i<nshape; i++) + { + shape2lib[i] = i; + lib2shape[i] = i; + JB2Shape &jshp = jim.get_shape(i); + libinfo[i].compute_bounding_box(*(jshp.bits)); + } +} + +int +JB2Dict::JB2Codec::add_library(const int shapeno, JB2Shape &jshp) +{ + const int libno = lib2shape.hbound() + 1; + lib2shape.touch(libno); + lib2shape[libno] = shapeno; + shape2lib.touch(shapeno); + shape2lib[shapeno] = libno; + libinfo.touch(libno); + libinfo[libno].compute_bounding_box(*(jshp.bits)); + return libno; +} + + +// CODE SIMPLE VALUES + +inline void +JB2Dict::JB2Codec::Decode::code_record_type(int &rectype) +{ + rectype=CodeNum( START_OF_DATA, END_OF_DATA, dist_record_type); +} + +int +JB2Dict::JB2Codec::Decode::code_match_index(int &index, JB2Dict &) +{ + int match=CodeNum(0, lib2shape.hbound(), dist_match_index); + index = lib2shape[match]; + return match; +} + + +// HANDLE SHORT LIST + +int +JB2Dict::JB2Codec::update_short_list(const int v) +{ + if (++ short_list_pos == 3) + short_list_pos = 0; + int * const s = short_list; + s[short_list_pos] = v; + + return (s[0] >= s[1]) + ?((s[0] > s[2])?((s[1] >= s[2])?s[1]:s[2]):s[0]) + :((s[0] < s[2])?((s[1] >= s[2])?s[2]:s[1]):s[0]); +} + + + +// CODE PAIRS + + +void +JB2Dict::JB2Codec::Decode::code_inherited_shape_count(JB2Dict &jim) +{ + int size=CodeNum(0, BIGPOSITIVE, inherited_shape_count_dist); + { + GP<JB2Dict> dict = jim.get_inherited_dict(); + if (!dict && size>0) + { + // Call callback function to obtain dictionary + if (cbfunc) + dict = (*cbfunc)(cbarg); + if (dict) + jim.set_inherited_dict(dict); + } + if (!dict && size>0) + G_THROW( ERR_MSG("JB2Image.need_dict") ); + if (dict && size!=dict->get_shape_count()) + G_THROW( ERR_MSG("JB2Image.bad_dict") ); + } +} + +void +JB2Dict::JB2Codec::Decode::code_image_size(JB2Dict &jim) +{ + int w=CodeNum(0, BIGPOSITIVE, image_size_dist); + int h=CodeNum(0, BIGPOSITIVE, image_size_dist); + if (w || h) + G_THROW( ERR_MSG("JB2Image.bad_dict2") ); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::code_image_size(JB2Dict &) +{ + last_left = 1; + last_row_left = 0; + last_row_bottom = 0; + last_right = 0; + fill_short_list(last_row_bottom); + gotstartrecordp = 1; +} + +void +JB2Dict::JB2Codec::Decode::code_image_size(JB2Image &jim) +{ + image_columns=CodeNum(0, BIGPOSITIVE, image_size_dist); + image_rows=CodeNum(0, BIGPOSITIVE, image_size_dist); + if (!image_columns || !image_rows) + G_THROW( ERR_MSG("JB2Image.zero_dim") ); + jim.set_dimension(image_columns, image_rows); + JB2Codec::code_image_size(jim); +} + +void +JB2Dict::JB2Codec::code_image_size(JB2Image &) +{ + last_left = 1 + image_columns; + last_row_left = 0; + last_row_bottom = image_rows; + last_right = 0; + fill_short_list(last_row_bottom); + gotstartrecordp = 1; +} + +inline int +JB2Dict::JB2Codec::Decode::get_diff(int,NumContext &rel_loc) +{ + return CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_loc); +} + +void +JB2Dict::JB2Codec::code_relative_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + // Find location + int bottom=0, left=0, top=0, right=0; + int x_diff, y_diff; + if (encoding) + { + left = jblt->left + 1; + bottom = jblt->bottom + 1; + right = left + columns - 1; + top = bottom + rows - 1; + } + // Code offset type + int new_row=CodeBit((left<last_left), offset_type_dist); + if (new_row) + { + // Begin a new row + x_diff=get_diff(left-last_row_left,rel_loc_x_last); + y_diff=get_diff(top-last_row_bottom,rel_loc_y_last); + if (!encoding) + { + left = last_row_left + x_diff; + top = last_row_bottom + y_diff; + right = left + columns - 1; + bottom = top - rows + 1; + } + last_left = last_row_left = left; + last_right = right; + last_bottom = last_row_bottom = bottom; + fill_short_list(bottom); + } + else + { + // Same row + x_diff=get_diff(left-last_right,rel_loc_x_current); + y_diff=get_diff(bottom-last_bottom,rel_loc_y_current); + if (!encoding) + { + left = last_right + x_diff; + bottom = last_bottom + y_diff; + right = left + columns - 1; + top = bottom + rows - 1; + } + last_left = left; + last_right = right; + last_bottom = update_short_list(bottom); + } + // Store in blit record + if (!encoding) + { + jblt->bottom = bottom - 1; + jblt->left = left - 1; + } +} + +void +JB2Dict::JB2Codec::Decode::code_absolute_location(JB2Blit *jblt, int rows, int columns) +{ + // Check start record + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + int left=CodeNum(1, image_columns, abs_loc_x); + int top=CodeNum(1, image_rows, abs_loc_y); + jblt->bottom = top - rows + 1 - 1; + jblt->left = left - 1; +} + +void +JB2Dict::JB2Codec::Decode::code_absolute_mark_size(GBitmap &bm, int border) +{ + int xsize=CodeNum(0, BIGPOSITIVE, abs_size_x); + int ysize=CodeNum(0, BIGPOSITIVE, abs_size_y); + if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize)) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + bm.init(ysize, xsize, border); +} + +void +JB2Dict::JB2Codec::Decode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border) +{ + int xdiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_x); + int ydiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_y); + int xsize = cw + xdiff; + int ysize = ch + ydiff; + if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize)) + G_THROW( ERR_MSG("JB2Image.bad_number") ); + bm.init(ysize, xsize, border); +} + + + + +// CODE BITMAP DIRECTLY + +void +JB2Dict::JB2Codec::code_bitmap_directly (GBitmap &bm) +{ + // Make sure bitmap will not be disturbed + GMonitorLock lock(bm.monitor()); + // ensure borders are adequate + bm.minborder(3); + // initialize row pointers + int dy = bm.rows() - 1; + code_bitmap_directly(bm,bm.columns(),dy,bm[dy+2],bm[dy+1],bm[dy]); +} + +void +JB2Dict::JB2Codec::Decode::code_bitmap_directly( + GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (decoding) + while (dy >= 0) + { + int context=get_direct_context(up2, up1, up0, 0); + for(int dx=0;dx < dw;) + { + int n = zp.decoder(bitdist[context]); + up0[dx++] = n; + context=shift_direct_context(context, n, up2, up1, up0, dx); + } + // next row + dy -= 1; + up2 = up1; + up1 = up0; + up0 = bm[dy]; + } +#ifndef NDEBUG + bm.check_border(); +#endif +} + + + + + +// CODE BITMAP BY CROSS CODING + +void +JB2Dict::JB2Codec::code_bitmap_by_cross_coding (GBitmap &bm, GP<GBitmap> &cbm, const int libno) +{ + // Make sure bitmaps will not be disturbed + GP<GBitmap> copycbm=GBitmap::create(); + if (cbm->monitor()) + { + // Perform a copy when the bitmap is explicitely shared + GMonitorLock lock2(cbm->monitor()); + copycbm->init(*cbm); + cbm = copycbm; + } + GMonitorLock lock1(bm.monitor()); + // Center bitmaps + const int cw = cbm->columns(); + const int dw = bm.columns(); + const int dh = bm.rows(); + const LibRect &l = libinfo[libno]; + const int xd2c = (dw/2 - dw + 1) - ((l.right - l.left + 1)/2 - l.right); + const int yd2c = (dh/2 - dh + 1) - ((l.top - l.bottom + 1)/2 - l.top); + // Ensure borders are adequate + bm.minborder(2); + cbm->minborder(2-xd2c); + cbm->minborder(2+dw+xd2c-cw); + // Initialize row pointers + const int dy = dh - 1; + const int cy = dy + yd2c; +#ifndef NDEBUG + bm.check_border(); + cbm->check_border(); +#endif + code_bitmap_by_cross_coding (bm,*cbm, xd2c, dw, dy, cy, bm[dy+1], bm[dy], + (*cbm)[cy+1] + xd2c, (*cbm)[cy ] + xd2c, (*cbm)[cy-1] + xd2c); +} + +void +JB2Dict::JB2Codec::Decode::code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 ) +{ + ZPCodec &zp=*gzp; + // iterate on rows (decoding) + while (dy >= 0) + { + int context=get_cross_context( + up1, up0, xup1, xup0, xdn1, 0); + for(int dx=0;dx < dw;) + { + const int n = zp.decoder(cbitdist[context]); + up0[dx++] = n; + context=shift_cross_context(context, n, + up1, up0, xup1, xup0, xdn1, dx); + } + // next row + up1 = up0; + up0 = bm[--dy]; + xup1 = xup0; + xup0 = xdn1; + xdn1 = cbm[(--cy)-1] + xd2c; +#ifndef NDEBUG + bm.check_border(); +#endif + } +} + + + + +// CODE JB2DICT RECORD + +void +JB2Dict::JB2Codec::code_record( + int &rectype, const GP<JB2Dict> &gjim, JB2Shape *xjshp) +{ + GP<GBitmap> cbm; + GP<GBitmap> bm; + int shapeno = -1; + + // Code record type + code_record_type(rectype); + + // Pre-coding actions + switch(rectype) + { + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + if (!encoding) + { + jshp.bits = GBitmap::create(); + jshp.parent = -1; + } + bm = jshp.bits; + break; + } + } + // Coding actions + switch (rectype) + { + case START_OF_DATA: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + code_image_size (jim); + code_eventual_lossless_refinement (); + if (! encoding) + init_library(jim); + break; + } + case NEW_MARK_LIBRARY_ONLY: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + break; + } + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + JB2Shape &jshp=*xjshp; + int match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, jshp.parent); + break; + } + case PRESERVED_COMMENT: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + code_comment(jim.comment); + break; + } + case REQUIRED_DICT_OR_RESET: + { + if (! gotstartrecordp) + { + // Indicates need for a shape dictionary + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + code_inherited_shape_count(*gjim); + }else + // Reset all numerical contexts to zero + reset_numcoder(); + break; + } + case END_OF_DATA: + { + break; + } + default: + { + G_THROW( ERR_MSG("JB2Image.bad_type") ); + } + } + // Post-coding action + if (!encoding) + { + // add shape to dictionary + switch(rectype) + { + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + shapeno = gjim->add_shape(jshp); + add_library(shapeno, jshp); + break; + } + } + // make sure everything is compacted + // decompaction will occur automatically when needed + if (bm) + bm->compress(); + } +} + + +// CODE JB2DICT + +void +JB2Dict::JB2Codec::Decode::code(const GP<JB2Dict> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Dict &jim=*gjim; + // ------------------------- + // THIS IS THE DECODING PART + // ------------------------- + int rectype; + JB2Shape tmpshape; + do + { + code_record(rectype, gjim, &tmpshape); + } + while(rectype != END_OF_DATA); + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + jim.compress(); +} + + + +// CODE JB2IMAGE RECORD + +void +JB2Dict::JB2Codec::code_record( + int &rectype, const GP<JB2Image> &gjim, JB2Shape *xjshp, JB2Blit *jblt) +{ + GP<GBitmap> bm; + GP<GBitmap> cbm; + int shapeno = -1; + int match; + + // Code record type + code_record_type(rectype); + + // Pre-coding actions + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + { + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + if (!encoding) + { + jshp.bits = GBitmap::create(); + jshp.parent = -1; + if (rectype == NON_MARK_DATA) + jshp.parent = -2; + } + bm = jshp.bits; + break; + } + } + // Coding actions + switch (rectype) + { + case START_OF_DATA: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + code_image_size (jim); + code_eventual_lossless_refinement (); + if (! encoding) + init_library(jim); + break; + } + case NEW_MARK: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case NEW_MARK_LIBRARY_ONLY: + { + code_absolute_mark_size (*bm, 4); + code_bitmap_directly (*bm); + break; + } + case NEW_MARK_IMAGE_ONLY: + { + code_absolute_mark_size (*bm, 3); + code_bitmap_directly (*bm); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_REFINE: + { + if(!xjshp || !gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + JB2Image &jim=*gjim; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, match); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_REFINE_LIBRARY_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + JB2Shape &jshp=*xjshp; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + break; + } + case MATCHED_REFINE_IMAGE_ONLY: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + JB2Shape &jshp=*xjshp; + match = code_match_index (jshp.parent, jim); + cbm = jim.get_shape(jshp.parent).bits; + LibRect &l = libinfo[match]; + code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4); + code_bitmap_by_cross_coding (*bm, cbm, match); + code_relative_location (jblt, bm->rows(), bm->columns() ); + break; + } + case MATCHED_COPY: + { + int temp; + if (encoding) temp = jblt->shapeno; + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + match = code_match_index (temp, jim); + if (!encoding) jblt->shapeno = temp; + bm = jim.get_shape(jblt->shapeno).bits; + LibRect &l = libinfo[match]; + jblt->left += l.left; + jblt->bottom += l.bottom; + if (jim.reproduce_old_bug) + code_relative_location (jblt, bm->rows(), bm->columns() ); + else + code_relative_location (jblt, l.top-l.bottom+1, l.right-l.left+1 ); + jblt->left -= l.left; + jblt->bottom -= l.bottom; + break; + } + case NON_MARK_DATA: + { + code_absolute_mark_size (*bm, 3); + code_bitmap_directly (*bm); + code_absolute_location (jblt, bm->rows(), bm->columns() ); + break; + } + case PRESERVED_COMMENT: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + code_comment(jim.comment); + break; + } + case REQUIRED_DICT_OR_RESET: + { + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + if (! gotstartrecordp) + // Indicates need for a shape dictionary + code_inherited_shape_count(jim); + else + // Reset all numerical contexts to zero + reset_numcoder(); + break; + } + case END_OF_DATA: + { + break; + } + default: + { + G_THROW( ERR_MSG("JB2Image.unknown_type") ); + } + } + + // Post-coding action + if (!encoding) + { + // add shape to image + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + { + if(!xjshp||!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Shape &jshp=*xjshp; + shapeno = gjim->add_shape(jshp); + shape2lib.touch(shapeno); + shape2lib[shapeno] = -1; + break; + } + } + // add shape to library + switch(rectype) + { + case NEW_MARK: + case NEW_MARK_LIBRARY_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_LIBRARY_ONLY: + if(!xjshp) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + add_library(shapeno, *xjshp); + break; + } + // make sure everything is compacted + // decompaction will occur automatically on cross-coding bitmaps + if (bm) + bm->compress(); + // add blit to image + switch (rectype) + { + case NEW_MARK: + case NEW_MARK_IMAGE_ONLY: + case MATCHED_REFINE: + case MATCHED_REFINE_IMAGE_ONLY: + case NON_MARK_DATA: + jblt->shapeno = shapeno; + case MATCHED_COPY: + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + gjim->add_blit(* jblt); + break; + } + } +} + + +// CODE JB2IMAGE + +void +JB2Dict::JB2Codec::Decode::code(const GP<JB2Image> &gjim) +{ + if(!gjim) + { + G_THROW( ERR_MSG("JB2Image.bad_number") ); + } + JB2Image &jim=*gjim; + // ------------------------- + // THIS IS THE DECODING PART + // ------------------------- + int rectype; + JB2Blit tmpblit; + JB2Shape tmpshape; + do + { + code_record(rectype, gjim, &tmpshape, &tmpblit); + } + while(rectype!=END_OF_DATA); + if (!gotstartrecordp) + G_THROW( ERR_MSG("JB2Image.no_start") ); + jim.compress(); +} + + + +//////////////////////////////////////// +//// HELPERS +//////////////////////////////////////// + +void +JB2Dict::JB2Codec::LibRect::compute_bounding_box(const GBitmap &bm) +{ + // First lock the stuff. + GMonitorLock lock(bm.monitor()); + // Get size + const int w = bm.columns(); + const int h = bm.rows(); + const int s = bm.rowsize(); + // Right border + for(right=w-1;right >= 0;--right) + { + unsigned char const *p = bm[0] + right; + unsigned char const * const pe = p+(s*h); + for (;(p<pe)&&(!*p);p+=s) + continue; + if (p<pe) + break; + } + // Top border + for(top=h-1;top >= 0;--top) + { + unsigned char const *p = bm[top]; + unsigned char const * const pe = p+w; + for (;(p<pe)&&(!*p); ++p) + continue; + if (p<pe) + break; + } + // Left border + for (left=0;left <= right;++left) + { + unsigned char const *p = bm[0] + left; + unsigned char const * const pe=p+(s*h); + for (;(p<pe)&&(!*p);p+=s) + continue; + if (p<pe) + break; + } + // Bottom border + for(bottom=0;bottom <= top;++bottom) + { + unsigned char const *p = bm[bottom]; + unsigned char const * const pe = p+w; + for (;(p<pe)&&(!*p); ++p) + continue; + if (p<pe) + break; + } +} + +GP<JB2Dict> +JB2Dict::create(void) +{ + return new JB2Dict(); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/JB2Image.h b/kviewshell/plugins/djvu/libdjvu/JB2Image.h new file mode 100644 index 00000000..f55ae451 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JB2Image.h @@ -0,0 +1,805 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JB2Image.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _JB2IMAGE_H +#define _JB2IMAGE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +/** @name JB2Image.h + + Files #"JB2Image.h"# and #"JB2Image.cpp"# address the compression of + bilevel images using the JB2 soft pattern matching scheme. These files + provide the complete decoder and the decoder back-end. The JB2 scheme is + optimized for images containing a large number of self-similar small + components such as characters. Typical text images can be compressed into + files 3 to 5 times smaller than with G4/MMR and 2 to 4 times smaller than + with JBIG1. + + {\bf JB2 and JBIG2} --- JB2 has strong similarities with the forthcoming + JBIG2 standard developed by the "ISO/IEC JTC1 SC29 Working Group 1" which + is responsible for both the JPEG and JBIG standards. This is hardly + surprising since JB2 was our own proposal for the JBIG2 standard + and remained the only proposal for years. The full JBIG2 standard however + is significantly more complex and slighlty less efficient than JB2 because + it addresses a broader range of applications. Full JBIG2 compliance may + be implemented in the future. + + {\bf JB2 Images} --- Class \Ref{JB2Image} is the central data structure + implemented here. A #JB2Image# is composed of an array of shapes + and an array of blits. Each shape contains a small bitmap representing an + elementary blob of ink, such as a character or a segment of line art. + Each blit instructs the decoder to render a particular shape at a + specified position in the image. Some compression is already achieved + because several blits can refer to the same shape. A shape can also + contain a pointer to a parent shape. Additional compression is achieved + when both shapes are similar because each shape is encoded using the + parent shape as a model. A #"O"# shape for instance could be a parent for + both a #"C"# shape and a #"Q"# shape. + + {\bf JB2 Dictionary} --- Class \Ref{JB2Dict} is a peculiar kind of + JB2Image which only contains an array of shapes. These shapes can be + referenced from another JB2Dict/JB2Image. This is arranged by setting the + ``inherited dictionary'' of a JB2Dict/JB2Image using function + \Ref{JB2Dict::set_inherited_dict}. Several JB2Images can use shapes from a + same JB2Dict encoded separately. This is how several pages of a same + document can share information. + + {\bf Decoding JB2 data} --- The first step for decoding JB2 data consists of + creating an empty #JB2Image# object. Function \Ref{JB2Image::decode} then + reads the data and populates the #JB2Image# with the shapes and the blits. + Function \Ref{JB2Image::get_bitmap} finally produces an anti-aliased image. + + {\bf Encoding JB2 data} --- The first step for decoding JB2 data also + consists of creating an empty #JB2Image# object. You must then use + functions \Ref{JB2Image::add_shape} and \Ref{JB2Image::add_blit} to + populate the #JB2Image# object. Function \Ref{JB2Image::encode} finally + produces the JB2 data. Function #encode# sequentially encodes the blits + and the necessary shapes. The compression ratio depends on several + factors: + \begin{itemize} + \item Blits should reuse shapes as often as possible. + \item Blits should be sorted in reading order because this facilitates + the prediction of the blit coordinates. + \item Shapes should be sorted according to the order of first appearance + in the sequence of blits because this facilitates the prediction of the + shape indices. + \item Shapes should be compared to all previous shapes in the shape array. + The shape parent pointer should be set to a suitable parent shape if + such a parent shape exists. The parent shape should have almost the + same size and the same pixels. + \end{itemize} + All this is quite easy to achieve in the case of an electronically + produced document such as a DVI file or a PS file: we know what the + characters are and where they are located. If you only have a scanned + image however you must first locate the characters (connected component + analysis) and cut the remaining pieces of ink into smaller blobs. + Ordering the blits and matching the shapes is then an essentially + heuristic process. Although the quality of the heuristics substantially + effects the file size, misordering blits or mismatching shapes never + effects the quality of the image. The last refinement consists in + smoothing the shapes in order to reduce the noise and maximize the + similarities between shapes. + + {\bf JB2 extensions} --- Two extensions of the JB2 + encoding format have been introduced with DjVu files version 21. The first + extension addresses the shared shape dictionaries. The second extension + bounds the number of probability contexts used for coding numbers. + Both extensions maintain backward compatibility with JB2 as + described in the ICFDD proposal. A more complete discussion + can be found in section \Ref{JB2 extensions for version 21.}. + + {\bf References} + \begin{itemize} + \item Paul G. Howard : {\em Text image compression using soft + pattern matching}, Computer Journal, volume 40:2/3, 1997. + \item JBIG1 : \URL{http://www.jpeg.org/public/jbighomepage.htm}. + \item JBIG2 draft : \URL{http://www.jpeg.org/public/jbigpt2.htm}. + \item ICFDD Draft Proposed American National Standard, 1999-08-26. + \end{itemize} + + @version + #$Id: JB2Image.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @memo + Coding bilevel images with JB2. + @author + Paul Howard <[email protected]> -- JB2 design\\ + L\'eon Bottou <[email protected]> -- this implementation + +// From: Leon Bottou, 1/31/2002 +// Lizardtech has split the corresponding cpp file into a decoder and an encoder. +// Only superficial changes. The meat is mine. + +*/ +//@{ + + +#include "GString.h" +#include "ZPCodec.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class JB2Dict; +class JB2Image; +class GRect; +class GBitmap; +class ByteStream; + +/** Blit data structure. A #JB2Image# contains an array of #JB2Blit# data + structures. Each array entry instructs the decoder to render a particular + shape at a particular location. Members #left# and #bottom# specify the + coordinates of the bottom left corner of the shape bitmap. All + coordinates are relative to the bottom left corner of the image. Member + #shapeno# is the subscript of the shape to be rendered. */ + +class JB2Blit { +public: + /** Horizontal coordinate of the blit. */ + unsigned short left; + /** Vertical coordinate of the blit. */ + unsigned short bottom; + /** Index of the shape to blit. */ + unsigned int shapeno; +}; + + +/** Shape data structure. A #JB2Image# contains an array of #JB2Shape# data + structures. Each array entry represents an elementary blob of ink such as + a character or a segment of line art. Member #bits# points to a bilevel + image representing the shape pixels. Member #parent# is the subscript of + the parent shape. */ + +class JB2Shape +{ +public: + /** Subscript of the parent shape. The parent shape must always be located + before the current shape in the shape array. A negative value indicates + that this shape has no parent. Any negative values smaller than #-1# + further indicates that this shape does not look like a character. This + is used to enable a few internal optimizations. This information is + saved into the JB2 file, but the actual value of the #parent# variable + is not. */ + int parent; + /** Bilevel image of the shape pixels. This must be a pointer to a bilevel + #GBitmap# image. This pointer can also be null. The encoder will just + silently discard all blits referring to a shape containing a null + bitmap. */ + GP<GBitmap> bits; + /** Private user data. This long word is provided as a convenience for users + of the JB2Image data structures. Neither the rendering functions nor + the coding functions ever access this value. */ + long userdata; +}; + + + +/** JB2 Dictionary callback. + The decoding function call this callback function when they discover that + the current JB2Image or JB2Dict needs a pre-existing shape dictionary. + The callback function must return a pointer to the dictionary or NULL + if none is found. */ + +typedef GP<JB2Dict> JB2DecoderCallback ( void* ); + + +/** Dictionary of JB2 shapes. */ + +class JB2Dict : public GPEnabled +{ +protected: + JB2Dict(void); +public: + class JB2Codec; + + // CONSTRUCTION + /** Default creator. Constructs an empty #JB2Dict# object. You can then + call the decoding function #decode#. You can also manually set the + image size using #add_shape#. */ + static GP<JB2Dict> create(void); + + // INITIALIZATION + /** Resets the #JB2Image# object. This function reinitializes both the shape + and the blit arrays. All allocated memory is freed. */ + void init(void); + + // INHERITED + /** Returns the inherited dictionary. */ + GP<JB2Dict> get_inherited_dict(void) const; + /** Returns the number of inherited shapes. */ + int get_inherited_shape_count(void) const; + /** Sets the inherited dictionary. */ + void set_inherited_dict(const GP<JB2Dict> &dict); + + // ACCESSING THE SHAPE LIBRARY + /** Returns the total number of shapes. + Shape indices range from #0# to #get_shape_count()-1#. */ + int get_shape_count(void) const; + /** Returns a pointer to shape #shapeno#. + The returned pointer directly points into the shape array. + This pointer can be used for reading or writing the shape data. */ + JB2Shape &get_shape(const int shapeno); + /** Returns a constant pointer to shape #shapeno#. + The returned pointer directly points into the shape array. + This pointer can only be used for reading the shape data. */ + const JB2Shape &get_shape(const int shapeno) const; + /** Appends a shape to the shape array. This function appends a copy of + shape #shape# to the shape array and returns the subscript of the new + shape. The subscript of the parent shape #shape.parent# must + actually designate an already existing shape. */ + int add_shape(const JB2Shape &shape); + + // MEMORY OPTIMIZATION + /** Compresses all shape bitmaps. This function reduces the memory required + by the #JB2Image# by calling \Ref{GBitmap::compress} on all shapes + bitmaps. This function is best called after decoding a #JB2Image#, + because function \Ref{get_bitmap} can directly use the compressed + bitmaps. */ + void compress(void); + /** Returns the total memory used by the JB2Image. + The returned value is expressed in bytes. */ + unsigned int get_memory_usage(void) const; + + // CODING + /** Encodes the JB2Dict into ByteStream #bs#. + This function generates the JB2 data stream without any header. */ + void encode(const GP<ByteStream> &gbs) const; + /** Decodes JB2 data from ByteStream #bs#. This function decodes the image + size and populates the shape and blit arrays. The callback function + #cb# is called when the decoder determines that the ByteStream data + requires a shape dictionary which has not been set with + \Ref{JB2Dict::set_inherited_dict}. The callback receives argument #arg# + and must return a suitable dictionary which will be installed as the + inherited dictionary. The callback should return null if no such + dictionary is found. */ + void decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb=0, void *arg=0); + + +public: + /** Comment string coded by JB2 file. */ + GUTF8String comment; + +private: + int inherited_shapes; + GP<JB2Dict> inherited_dict; + GArray<JB2Shape> shapes; + +}; + +/** Main JB2 data structure. Each #JB2Image# consists of an array of shapes + and an array of blits. These arrays can be populated by hand using + functions \Ref{add_shape} and \Ref{add_blit}, or by decoding JB2 data + using function \Ref{decode}. You can then use function \Ref{get_bitmap} + to render anti-aliased images, or use function \Ref{encode} to generate + JB2 data. */ + +class JB2Image : public JB2Dict +{ +protected: + JB2Image(void); +public: + + /** Creates an empty #JB2Image# object. You can then + call the decoding function #decode#. You can also manually set the + image size using #set_dimension# and populate the shape and blit arrays + using #add_shape# and #add_blit#. */ + static GP<JB2Image> create(void) { return new JB2Image(); } + + // INITIALIZATION + /** Resets the #JB2Image# object. This function reinitializes both the shape + and the blit arrays. All allocated memory is freed. */ + void init(void); + + // DIMENSION + /** Returns the width of the image. + This is the width value previously set with #set_dimension#. */ + int get_width(void) const; + /** Returns the height of the image. + This is the height value previously set with #set_dimension#. */ + int get_height(void) const; + /** Sets the size of the JB2Image. + This function can be called at any time. + The corresponding #width# and the #height# are stored + in the JB2 file. */ + void set_dimension(int width, int height); + + // RENDERING + /** Renders an anti-aliased gray level image. This function renders the + JB2Image as a bilevel or gray level image. Argument #subsample# + specifies the desired subsampling ratio in range #1# to #15#. The + returned image uses #1+subsample^2# gray levels for representing + anti-aliased edges. Argument #align# specified the alignment of the + rows of the returned images. Setting #align# to #4#, for instance, will + adjust the bitmap border in order to make sure that each row of the + returned image starts on a word (four byte) boundary. */ + GP<GBitmap> get_bitmap(int subsample = 1, int align = 1) const; + /** Renders an anti-aliased gray level sub-image. This function renders a + segment of the JB2Image as a bilevel or gray level image. Conceptually, + this function first renders the full JB2Image with subsampling ratio + #subsample# and then extracts rectangle #rect# in the subsampled image. + Both operations of course are efficiently performed simultaneously. + Argument #align# specified the alignment of the rows of the returned + images, as explained above. Argument #dispy# should remain null. */ + GP<GBitmap> get_bitmap(const GRect &rect, int subsample=1, int align=1, int dispy=0) const; + + // ACCESSING THE BLIT LIBRARY + /** Returns the total number of blits. + Blit indices range from #0# to #get_blit_count(void)-1#. */ + int get_blit_count(void) const; + /** Returns a pointer to blit #blitno#. + The returned pointer directly points into the blit array. + This pointer can be used for reading or writing the blit data. */ + JB2Blit *get_blit(int blitno); + /** Returns a constant pointer to blit #blitno#. + The returned pointer directly points into the shape array. + This pointer can only be used for reading the shape data. */ + const JB2Blit *get_blit(int blitno) const; + /** Appends a blit to the blit array. This function appends a copy of blit + #blit# to the blit array and returns the subscript of the new blit. The + shape subscript #blit.shapeno# must actually designate an already + existing shape. */ + int add_blit(const JB2Blit &blit); + + // MEMORY OPTIMIZATION + /** Returns the total memory used by the JB2Image. + The returned value is expressed in bytes. */ + unsigned int get_memory_usage(void) const; + + // CODING + /** Encodes the JB2Image into ByteStream #bs#. + This function generates the JB2 data stream without any header. */ + void encode(const GP<ByteStream> &gbs) const; + /** Decodes JB2 data from ByteStream #bs#. This function decodes the image + size and populates the shape and blit arrays. The callback function + #cb# is called when the decoder determines that the ByteStream data + requires a shape dictionary which has not been set with + \Ref{JB2Dict::set_inherited_dict}. The callback receives argument #arg# + and must return a suitable dictionary which will be installed as the + inherited dictionary. The callback should return null if no such + dictionary is found. */ + void decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb=0, void *arg=0); + +private: + // Implementation + int width; + int height; + GTArray<JB2Blit> blits; +public: + /** Reproduces a old bug. Setting this flag may be necessary for accurately + decoding DjVu files with version smaller than #18#. The default value + is of couse #false#. */ + bool reproduce_old_bug; +}; + + + +// JB2DICT INLINE FUNCTIONS + +inline int +JB2Dict::get_shape_count(void) const +{ + return inherited_shapes + shapes.size(); +} + +inline int +JB2Dict::get_inherited_shape_count(void) const +{ + return inherited_shapes; +} + +inline GP<JB2Dict> +JB2Dict::get_inherited_dict(void) const +{ + return inherited_dict; +} + +// JB2IMAGE INLINE FUNCTIONS + +inline int +JB2Image::get_width(void) const +{ + return width; +} + +inline int +JB2Image::get_height(void) const +{ + return height; +} + + +inline int +JB2Image::get_blit_count(void) const +{ + return blits.size(); +} + + +inline JB2Blit * +JB2Image::get_blit(int blitno) +{ + return & blits[blitno]; +} + +inline const JB2Blit * +JB2Image::get_blit(int blitno) const +{ + return & blits[blitno]; +} + + + + + +/** @name JB2 extensions for version 21. + + Two extensions of the JB2 encoding format have been introduced + with DjVu files version 21. Both extensions maintain significant + backward compatibility with previous version of the JB2 format. + These extensions are described below by reference to the ICFDD + proposal dated August 1999. Both extension make use of the unused + record type value #9# (cf. ICFDD page 24) which has been renamed + #REQUIRED_DICT_OR_RESET#. + + {\bf Shared Shape Dictionaries} --- This extension provides + support for sharing symbol definitions between the pages of a + document. To achieve this objective, the JB2 image data chunk + must be able to address symbols defined elsewhere by a JB2 + dictionary data chunk shared by all the pages of a document. + + The arithmetically encoded JB2 image data logically consist of a + sequence of records. The decoder processes these records in + sequence and maintains a library of symbols which can be addressed + by the following records. The first record usually is a ``Start + Of Image'' record describing the size of the image. + + Starting with version 21, a #REQUIRED_DICT_OR_RESET# (9) record + type can appear {\em before} the #START_OF_DATA# (0) record. The + record type field is followed by a single number arithmetically + encoded (cf. ICFDD page 26) using a sixteenth context (cf. ICFDD + page 25). This record appears when the JB2 data chunk requires + symbols encoded in a separate JB2 dictionary data chunk. The + number (the {\bf dictionary size}) indicates how many symbols + should have been defined by the JB2 dictionary data chunk. The + decoder should simply load these symbols in the symbol library and + proceed as usual. New symbols potentially defined by the + subsequent JB2 image data records will therefore be numbered with + integers greater or equal than the dictionary size. + + The JB2 dictionary data format is a pure subset of the JB2 image + data format. The #START_OF_DATA# (0) record always specifies an + image width of zero and an image height of zero. The only allowed + record types are those defining library symbols only + (#NEW_SYMBOL_LIBRARY_ONLY# (2) and #MATCHED_REFINE_LIBRARY_ONLY# + (5) cf. ICFDD page 24) followed by a final #END_OF_DATA# (11) + record. + + The JB2 dictionary data is usually located in an {\bf Djbz} chunk. + Each page {\bf FORM:DJVU} may directly contain a {\bf Djbz} chunk, + or may indirectly point to such a chunk using an {\bf INCL} chunk + (cf. \Ref{Multipage DjVu documents.}). + + + {\bf Numcoder Reset} --- This extension addresses a problem for + hardware implementations. The encoding of numbers (cf. ICFDD page + 26) potentially uses an unbounded number of binary coding + contexts. These contexts are normally allocated when they are used + for the first time (cf. ICFDD informative note, page 27). + + Starting with version 21, a #REQUIRED_DICT_OR_RESET# (9) record + type can appear {\em after} the #START_OF_DATA# (0) record. The + decoder should proceed with the next record after {\em clearing + all binary contexts used for coding numbers}. This operation + implies that all binary contexts previously allocated for coding + numbers can be deallocated. + + Starting with version 21, the JB2 encoder should insert a + #REQUIRED_DICT_OR_RESET# record type whenever the number of these + allocated binary contexts exceeds #20000#. Only very large + documents ever reach such a large number of allocated binary + contexts (e.g large maps). Hardware implementation however can + benefit greatly from a hard bound on the total number of binary + coding contexts. Old JB2 decoders will treat this record type as + an #END_OF_DATA# record and cleanly stop decoding (cf. ICFDD page + 30, Image refinement data). + + + {\bf References} --- + \begin{itemize} + \item ICFDD Draft Proposed American National Standard, 1999-08-26. + \item DjVu Specification, \URL{http://www.lizardtech.com/djvu/sci/djvuspec}. + \end{itemize} + + @memo Extensions to the JB2 format introduced in version 21. */ + +//@} + +//////////////////////////////////////// +//// CLASS JB2CODEC: DECLARATION +//////////////////////////////////////// + +// This class is accessed via the encode and decode +// functions of class JB2Image + + +//**** Class JB2Codec +// This class implements the base class for both the JB2 coder and decoder. +// The JB2Codec's Contains all contextual information for encoding/decoding +// a JB2Image. + +class JB2Dict::JB2Codec +{ +public: + class Decode; + class Encode; + typedef unsigned int NumContext; + struct LibRect + { + int top,left,right,bottom; + void compute_bounding_box(const GBitmap &cbm); + }; + virtual ~JB2Codec(); +protected: + // Constructors + JB2Codec(const bool xencoding=false); + // Forbidden assignment + JB2Codec(const JB2Codec &ref); + JB2Codec& operator=(const JB2Codec &ref); + + int CodeNum(int lo, int hi, NumContext *pctx,int v); + void reset_numcoder(void); + inline void code_eventual_lossless_refinement(void); + void init_library(JB2Dict &jim); + int add_library(const int shapeno, JB2Shape &jshp); + void code_relative_location(JB2Blit *jblt, int rows, int columns); + void code_bitmap_directly (GBitmap &bm); + void code_bitmap_by_cross_coding (GBitmap &bm, GP<GBitmap> &cbm, const int libno); + void code_record(int &rectype, const GP<JB2Dict> &jim, JB2Shape *jshp); + void code_record(int &rectype, const GP<JB2Image> &jim, JB2Shape *jshp, JB2Blit *jblt); + static void compute_bounding_box(GBitmap &cbm, LibRect &lrect); + static int get_direct_context( unsigned char const * const up2, + unsigned char const * const up1, unsigned char const * const up0, + const int column); + static int shift_direct_context ( const int context, + const int next, unsigned char const * const up2, + unsigned char const * const up1, unsigned char const * const up0, + const int column); + static int get_cross_context( unsigned char const * const up1, + unsigned char const * const up0, unsigned char const * const xup1, + unsigned char const * const xup0, unsigned char const * const xdn1, + const int column ); + static int shift_cross_context( const int context, + const int n, unsigned char const * const up1, + unsigned char const * const up0, unsigned char const * const xup1, + unsigned char const * const xup0, unsigned char const * const xdn1, + const int column ); + + virtual bool CodeBit(const bool bit, BitContext &ctx) = 0; + virtual void code_comment(GUTF8String &comment) = 0; + virtual void code_record_type(int &rectype) = 0; + virtual int code_match_index(int &index, JB2Dict &jim)=0; + virtual void code_inherited_shape_count(JB2Dict &jim)=0; + virtual void code_image_size(JB2Dict &jim); + virtual void code_image_size(JB2Image &jim); + virtual void code_absolute_location(JB2Blit *jblt, int rows, int columns)=0; + virtual void code_absolute_mark_size(GBitmap &bm, int border=0) = 0; + virtual void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0) = 0; + virtual void code_bitmap_directly(GBitmap &bm,const int dw, int dy, + unsigned char *up2, unsigned char *up1, unsigned char *up0 )=0; + virtual void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm, + const int xd2c, const int dw, int dy, int cy, + unsigned char *up1, unsigned char *up0, unsigned char *xup1, + unsigned char *xup0, unsigned char *xdn1 )=0; + // Code records + virtual int get_diff(const int x_diff,NumContext &rel_loc) = 0; + +private: + bool encoding; + +protected: + // NumCoder + int cur_ncell; + BitContext *bitcells; + GPBuffer<BitContext> gbitcells; + NumContext *leftcell; + GPBuffer<NumContext> gleftcell; + NumContext *rightcell; + GPBuffer<NumContext> grightcell; + // Info + bool refinementp; + char gotstartrecordp; + // Code comment + NumContext dist_comment_byte; + NumContext dist_comment_length; + // Code values + NumContext dist_record_type; + NumContext dist_match_index; + BitContext dist_refinement_flag; + // Library + GTArray<int> shape2lib; + GTArray<int> lib2shape; + GTArray<LibRect> libinfo; + // Code pairs + NumContext abs_loc_x; + NumContext abs_loc_y; + NumContext abs_size_x; + NumContext abs_size_y; + NumContext image_size_dist; + NumContext inherited_shape_count_dist; + BitContext offset_type_dist; + NumContext rel_loc_x_current; + NumContext rel_loc_x_last; + NumContext rel_loc_y_current; + NumContext rel_loc_y_last; + NumContext rel_size_x; + NumContext rel_size_y; + int last_bottom; + int last_left; + int last_right; + int last_row_bottom; + int last_row_left; + int image_columns; + int image_rows; + int short_list[3]; + int short_list_pos; + inline void fill_short_list(const int v); + int update_short_list(const int v); + // Code bitmaps + BitContext bitdist[1024]; + BitContext cbitdist[2048]; +}; + +inline void +JB2Dict::JB2Codec::code_eventual_lossless_refinement(void) +{ + refinementp=CodeBit(refinementp, dist_refinement_flag); +} + +inline void +JB2Dict::JB2Codec::fill_short_list(const int v) +{ + short_list[0] = short_list[1] = short_list[2] = v; + short_list_pos = 0; +} + +inline int +JB2Dict::JB2Codec::get_direct_context( unsigned char const * const up2, + unsigned char const * const up1, + unsigned char const * const up0, + const int column) +{ + return ( (up2[column - 1] << 9) | + (up2[column ] << 8) | + (up2[column + 1] << 7) | + (up1[column - 2] << 6) | + (up1[column - 1] << 5) | + (up1[column ] << 4) | + (up1[column + 1] << 3) | + (up1[column + 2] << 2) | + (up0[column - 2] << 1) | + (up0[column - 1] << 0) ); +} + +inline int +JB2Dict::JB2Codec::shift_direct_context(const int context, const int next, + unsigned char const * const up2, + unsigned char const * const up1, + unsigned char const * const up0, + const int column) +{ + return ( ((context << 1) & 0x37a) | + (up1[column + 2] << 2) | + (up2[column + 1] << 7) | + (next << 0) ); +} + +inline int +JB2Dict::JB2Codec::get_cross_context( unsigned char const * const up1, + unsigned char const * const up0, + unsigned char const * const xup1, + unsigned char const * const xup0, + unsigned char const * const xdn1, + const int column ) +{ + return ( ( up1[column - 1] << 10) | + ( up1[column ] << 9) | + ( up1[column + 1] << 8) | + ( up0[column - 1] << 7) | + (xup1[column ] << 6) | + (xup0[column - 1] << 5) | + (xup0[column ] << 4) | + (xup0[column + 1] << 3) | + (xdn1[column - 1] << 2) | + (xdn1[column ] << 1) | + (xdn1[column + 1] << 0) ); +} + +inline int +JB2Dict::JB2Codec::shift_cross_context( const int context, const int n, + unsigned char const * const up1, + unsigned char const * const up0, + unsigned char const * const xup1, + unsigned char const * const xup0, + unsigned char const * const xdn1, + const int column ) +{ + return ( ((context<<1) & 0x636) | + ( up1[column + 1] << 8) | + (xup1[column ] << 6) | + (xup0[column + 1] << 3) | + (xdn1[column + 1] << 0) | + (n << 7) ); +} + +// ---------- THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp new file mode 100644 index 00000000..3f611965 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp @@ -0,0 +1,413 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JPEGDecoder.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#ifdef NEED_JPEG_DECODER + +#include "JPEGDecoder.h" + +#ifdef __cplusplus +extern "C" { +#endif +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H +#include <stdio.h> +#include <jconfig.h> +#include <jpeglib.h> +#include <jerror.h> +#ifdef __cplusplus +} +#endif + +#include "ByteStream.h" +#include "GPixmap.h" +#ifdef LIBJPEGNAME +#include "DjVuDynamic.h" +#include "GString.h" +#endif // LIBJPEGNAME + + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +class JPEGDecoder::Impl : public JPEGDecoder +{ +public: + static void jpeg_byte_stream_src(j_decompress_ptr, ByteStream &); +}; + +extern "C" +{ + +struct djvu_error_mgr +{ + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct djvu_error_mgr * djvu_error_ptr; + +METHODDEF(void) +djvu_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a djvu_error_mgr struct, so coerce pointer */ + djvu_error_ptr djvuerr = (djvu_error_ptr) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + /* Return control to the setjmp point */ + longjmp(djvuerr->setjmp_buffer, 1); +} + +} + +GP<GPixmap> +JPEGDecoder::decode(ByteStream & bs ) +{ + GP<GPixmap> retval=GPixmap::create(); + G_TRY + { + decode(bs,*retval); + } G_CATCH_ALL + { + retval=0; + } + G_ENDCATCH; + return retval; +} + +void +JPEGDecoder::decode(ByteStream & bs,GPixmap &pix) +{ + struct jpeg_decompress_struct cinfo; + + /* We use our private extension JPEG error handler. */ + struct djvu_error_mgr jerr; + + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + int full_buf_size; + int isGrey,i; + + cinfo.err = jpeg_std_error(&jerr.pub); + + jerr.pub.error_exit = djvu_error_exit; + + if (setjmp(jerr.setjmp_buffer)) + { + + jpeg_destroy_decompress(&cinfo); + G_THROW( ERR_MSG("GPixmap.unk_PPM") ); + } + + jpeg_create_decompress(&cinfo); + + Impl::jpeg_byte_stream_src(&cinfo, bs); + + (void) jpeg_read_header(&cinfo, TRUE); + + jpeg_start_decompress(&cinfo); + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + full_buf_size = row_stride * cinfo.output_height; + + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + GP<ByteStream> goutputBlock=ByteStream::create(); + ByteStream &outputBlock=*goutputBlock; + outputBlock.format("P6\n%d %d\n%d\n",cinfo.output_width, + cinfo.output_height,255); + + isGrey = ( cinfo.out_color_space == JCS_GRAYSCALE) ? 1 : 0; + + while (cinfo.output_scanline < cinfo.output_height) + { + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + + if ( isGrey == 1 ) + { + for (i=0; i<row_stride; i++) + { + outputBlock.write8((char)buffer[0][i]); + outputBlock.write8((char)buffer[0][i]); + outputBlock.write8((char)buffer[0][i]); + } + }else + { + for (i=0; i<row_stride; i++) + outputBlock.write8((char)buffer[0][i]); + } + } + + (void) jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + + outputBlock.seek(0,SEEK_SET); + + pix.init(outputBlock); +} + +/*** From here onwards code is to make ByteStream as the data + source for the JPEG library */ + +extern "C" +{ + +typedef struct +{ + struct jpeg_source_mgr pub; /* public fields */ + + ByteStream * byteStream; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_stream; +} byte_stream_src_mgr; + + +typedef byte_stream_src_mgr * byte_stream_src_ptr; + +#define INPUT_BUF_SIZE 4096 + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + + src->start_of_stream = TRUE; +} + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = src->byteStream->readall(src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) + { + if (src->start_of_stream) /* Treat empty input as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_stream = FALSE; + + return TRUE; +} + + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + byte_stream_src_ptr src = (byte_stream_src_ptr) cinfo->src; + + if (num_bytes > (long) src->pub.bytes_in_buffer) + { + src->byteStream->seek((num_bytes - src->pub.bytes_in_buffer), SEEK_CUR); + (void) fill_input_buffer(cinfo); + }else + { + src->pub.bytes_in_buffer -= num_bytes; + src->pub.next_input_byte += num_bytes; + } +} + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + +} + +void +JPEGDecoder::Impl::jpeg_byte_stream_src(j_decompress_ptr cinfo,ByteStream &bs) +{ + byte_stream_src_ptr src; + + if (cinfo->src == NULL) + { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(byte_stream_src_mgr)); + src = (byte_stream_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET)); + } + + src = (byte_stream_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->byteStream = &bs; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + +#ifdef LIBJPEGNAME +void * +JPEGDecoder::jpeg_lookup(const GUTF8String &name) +{ + static DjVuDynamic lib(GUTF8String(LIBJPEGNAME)); + void *sym=lib.lookup(name); + if(!sym) + G_THROW(ERR_MSG("DjVuFile.JPEG_bg2")); + return sym; +} + +jpeg_error_mgr * +JPEGDecoder::jpeg_std_error(jpeg_error_mgr *x) +{ + static void *sym=jpeg_lookup("jpeg_std_error"); + return ((jpeg_error_mgr *(*)(jpeg_error_mgr *))sym)(x); +} + +void +JPEGDecoder::jpeg_CreateDecompress(jpeg_decompress_struct *x,int v, size_t s) +{ + static void *sym=jpeg_lookup("jpeg_CreateDecompress"); + ((void (*)(jpeg_decompress_struct *,int,size_t))sym)(x,v,s); +} + +void +JPEGDecoder::jpeg_destroy_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_destroy_decompress"); + ((void (*)(j_decompress_ptr))sym)(x); +} + +int +JPEGDecoder::jpeg_read_header(j_decompress_ptr x,boolean y) +{ + static void *sym=jpeg_lookup("jpeg_read_header"); + return ((int (*)(j_decompress_ptr,boolean))sym)(x,y); +} + +JDIMENSION +JPEGDecoder::jpeg_read_scanlines(j_decompress_ptr x,JSAMPARRAY y,JDIMENSION z) +{ + static void *sym=jpeg_lookup("jpeg_read_scanlines"); + return ((JDIMENSION (*)(j_decompress_ptr,JSAMPARRAY,JDIMENSION))sym)(x,y,z); +} + +boolean +JPEGDecoder::jpeg_finish_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_finish_decompress"); + return ((boolean (*)(j_decompress_ptr))sym)(x); +} + +boolean +JPEGDecoder::jpeg_resync_to_restart(jpeg_decompress_struct *x,int d) +{ + static void *sym=jpeg_lookup("jpeg_resync_to_restart"); + return ((boolean (*)(jpeg_decompress_struct *,int))sym)(x,d); +} + +boolean +JPEGDecoder::jpeg_start_decompress(j_decompress_ptr x) +{ + static void *sym=jpeg_lookup("jpeg_start_decompress"); + return ((boolean (*)(j_decompress_ptr))sym)(x); +} + +#endif // LIBJPEGNAME + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h new file mode 100644 index 00000000..8a0ace5d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h @@ -0,0 +1,133 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: JPEGDecoder.h,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _JPEGDECODER_H_ +#define _JPEGDECODER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#ifdef NEED_JPEG_DECODER + +#include <string.h> +#include <setjmp.h> + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class GPixmap; + + +/** @name JPEGDecoder.h + Files #"JPEGDecoder.h"# and #"JPEGDecoder.cpp"# implement an + interface to the decoding subset of the IJG JPEG library. + @memo + Decoding interface to the IJG JPEG library. + @version + #$Id: JPEGDecoder.h,v 1.8 2003/11/07 22:08:22 leonb Exp $# + @author + Parag Deshmukh <[email protected]> +*/ +//@{ + +class GUTF8String; + +/** This class ensures namespace isolation. */ +class JPEGDecoder +{ +public: + class Impl; + + /** Decodes the JPEG formated ByteStream */ + static GP<GPixmap> decode(ByteStream & bs); + static void decode(ByteStream & bs,GPixmap &pix); +#ifdef LIBJPEGNAME + static void *jpeg_lookup(const GUTF8String &name); + static jpeg_error_mgr *jpeg_std_error(jpeg_error_mgr *x); + static void jpeg_CreateDecompress(jpeg_decompress_struct *x,int v, size_t s); + static void jpeg_destroy_decompress(j_decompress_ptr x); + static int jpeg_read_header(j_decompress_ptr x,boolean y); + static JDIMENSION jpeg_read_scanlines(j_decompress_ptr x,JSAMPARRAY y,JDIMENSION z); + static boolean jpeg_finish_decompress(j_decompress_ptr x); + static boolean jpeg_resync_to_restart(jpeg_decompress_struct *x,int d); + static boolean jpeg_start_decompress(j_decompress_ptr x); +#endif // LIBJPEGNAME +}; + + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif // NEED_JPEG_DECODER +#endif // _JPEGDECODER_H_ + diff --git a/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp new file mode 100644 index 00000000..b14220d1 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp @@ -0,0 +1,961 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMRDecoder.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "MMRDecoder.h" +#include "JB2Image.h" +#include "ByteStream.h" +#include "GBitmap.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// MMR CODEBOOKS + +static const char invalid_mmr_data[]= ERR_MSG("MMRDecoder.bad_data"); + +struct VLCode +{ + unsigned short code; + short codelen; + short value; +}; + +enum MMRMode +{ + P=0, H=1, V0=2, VR1=3, VR2=4, VR3=5, VL1=6, VL2=7, VL3=8 +}; + +static const VLCode mrcodes[] = +{ // Codes on 7 bits + // 7 bit codes + { 0x08, 4, P }, // 0001 + { 0x10, 3, H }, // 001 + { 0x40, 1, V0 }, // 1 + { 0x30, 3, VR1 }, // 011 + { 0x06, 6, VR2 }, // 000011 + { 0x03, 7, VR3 }, // 0000011 + { 0x20, 3, VL1 }, // 010 + { 0x04, 6, VL2 }, // 000010 + { 0x02, 7, VL3 }, // 0000010 + { 0x00, 0, -1 } // Illegal entry +}; + + +static const VLCode wcodes[] = { + // 13 bit codes + { 0x06a0, 8, 0 }, // 00110101 + { 0x0380, 6, 1 }, // 000111 + { 0x0e00, 4, 2 }, // 0111 + { 0x1000, 4, 3 }, // 1000 + { 0x1600, 4, 4 }, // 1011 + { 0x1800, 4, 5 }, // 1100 + { 0x1c00, 4, 6 }, // 1110 + { 0x1e00, 4, 7 }, // 1111 + { 0x1300, 5, 8 }, // 10011 + { 0x1400, 5, 9 }, // 10100 + { 0x0700, 5, 10 }, // 00111 + { 0x0800, 5, 11 }, // 01000 + { 0x0400, 6, 12 }, // 001000 + { 0x0180, 6, 13 }, // 000011 + { 0x1a00, 6, 14 }, // 110100 + { 0x1a80, 6, 15 }, // 110101 + { 0x1500, 6, 16 }, // 101010 + { 0x1580, 6, 17 }, // 101011 + { 0x09c0, 7, 18 }, // 0100111 + { 0x0300, 7, 19 }, // 0001100 + { 0x0200, 7, 20 }, // 0001000 + { 0x05c0, 7, 21 }, // 0010111 + { 0x00c0, 7, 22 }, // 0000011 + { 0x0100, 7, 23 }, // 0000100 + { 0x0a00, 7, 24 }, // 0101000 + { 0x0ac0, 7, 25 }, // 0101011 + { 0x04c0, 7, 26 }, // 0010011 + { 0x0900, 7, 27 }, // 0100100 + { 0x0600, 7, 28 }, // 0011000 + { 0x0040, 8, 29 }, // 00000010 + { 0x0060, 8, 30 }, // 00000011 + { 0x0340, 8, 31 }, // 00011010 + { 0x0360, 8, 32 }, // 00011011 + { 0x0240, 8, 33 }, // 00010010 + { 0x0260, 8, 34 }, // 00010011 + { 0x0280, 8, 35 }, // 00010100 + { 0x02a0, 8, 36 }, // 00010101 + { 0x02c0, 8, 37 }, // 00010110 + { 0x02e0, 8, 38 }, // 00010111 + { 0x0500, 8, 39 }, // 00101000 + { 0x0520, 8, 40 }, // 00101001 + { 0x0540, 8, 41 }, // 00101010 + { 0x0560, 8, 42 }, // 00101011 + { 0x0580, 8, 43 }, // 00101100 + { 0x05a0, 8, 44 }, // 00101101 + { 0x0080, 8, 45 }, // 00000100 + { 0x00a0, 8, 46 }, // 00000101 + { 0x0140, 8, 47 }, // 00001010 + { 0x0160, 8, 48 }, // 00001011 + { 0x0a40, 8, 49 }, // 01010010 + { 0x0a60, 8, 50 }, // 01010011 + { 0x0a80, 8, 51 }, // 01010100 + { 0x0aa0, 8, 52 }, // 01010101 + { 0x0480, 8, 53 }, // 00100100 + { 0x04a0, 8, 54 }, // 00100101 + { 0x0b00, 8, 55 }, // 01011000 + { 0x0b20, 8, 56 }, // 01011001 + { 0x0b40, 8, 57 }, // 01011010 + { 0x0b60, 8, 58 }, // 01011011 + { 0x0940, 8, 59 }, // 01001010 + { 0x0960, 8, 60 }, // 01001011 + { 0x0640, 8, 61 }, // 00110010 + { 0x0660, 8, 62 }, // 00110011 + { 0x0680, 8, 63 }, // 00110100 + { 0x1b00, 5, 64 }, // 11011 + { 0x1200, 5, 128 }, // 10010 + { 0x0b80, 6, 192 }, // 010111 + { 0x0dc0, 7, 256 }, // 0110111 + { 0x06c0, 8, 320 }, // 00110110 + { 0x06e0, 8, 384 }, // 00110111 + { 0x0c80, 8, 448 }, // 01100100 + { 0x0ca0, 8, 512 }, // 01100101 + { 0x0d00, 8, 576 }, // 01101000 + { 0x0ce0, 8, 640 }, // 01100111 + { 0x0cc0, 9, 704 }, // 011001100 + { 0x0cd0, 9, 768 }, // 011001101 + { 0x0d20, 9, 832 }, // 011010010 + { 0x0d30, 9, 896 }, // 011010011 + { 0x0d40, 9, 960 }, // 011010100 + { 0x0d50, 9, 1024 }, // 011010101 + { 0x0d60, 9, 1088 }, // 011010110 + { 0x0d70, 9, 1152 }, // 011010111 + { 0x0d80, 9, 1216 }, // 011011000 + { 0x0d90, 9, 1280 }, // 011011001 + { 0x0da0, 9, 1344 }, // 011011010 + { 0x0db0, 9, 1408 }, // 011011011 + { 0x0980, 9, 1472 }, // 010011000 + { 0x0990, 9, 1536 }, // 010011001 + { 0x09a0, 9, 1600 }, // 010011010 + { 0x0c00, 6, 1664 }, // 011000 (what did they think?) + { 0x09b0, 9, 1728 }, // 010011011 + { 0x0020, 11, 1792 }, // 00000001000 + { 0x0030, 11, 1856 }, // 00000001100 + { 0x0034, 11, 1920 }, // 00000001101 + { 0x0024, 12, 1984 }, // 000000010010 + { 0x0026, 12, 2048 }, // 000000010011 + { 0x0028, 12, 2112 }, // 000000010100 + { 0x002a, 12, 2176 }, // 000000010101 + { 0x002c, 12, 2240 }, // 000000010110 + { 0x002e, 12, 2304 }, // 000000010111 + { 0x0038, 12, 2368 }, // 000000011100 + { 0x003a, 12, 2432 }, // 000000011101 + { 0x003c, 12, 2496 }, // 000000011110 + { 0x003e, 12, 2560 }, // 000000011111 + { 0x0000, 0, -1 } // Illegal entry +}; + + +static const VLCode bcodes[] = { + // 13 bit codes + { 0x01b8, 10, 0 }, // 0000110111 + { 0x0800, 3, 1 }, // 010 + { 0x1800, 2, 2 }, // 11 + { 0x1000, 2, 3 }, // 10 + { 0x0c00, 3, 4 }, // 011 + { 0x0600, 4, 5 }, // 0011 + { 0x0400, 4, 6 }, // 0010 + { 0x0300, 5, 7 }, // 00011 + { 0x0280, 6, 8 }, // 000101 + { 0x0200, 6, 9 }, // 000100 + { 0x0100, 7, 10 }, // 0000100 + { 0x0140, 7, 11 }, // 0000101 + { 0x01c0, 7, 12 }, // 0000111 + { 0x0080, 8, 13 }, // 00000100 + { 0x00e0, 8, 14 }, // 00000111 + { 0x0180, 9, 15 }, // 000011000 + { 0x00b8, 10, 16 }, // 0000010111 + { 0x00c0, 10, 17 }, // 0000011000 + { 0x0040, 10, 18 }, // 0000001000 + { 0x019c, 11, 19 }, // 00001100111 + { 0x01a0, 11, 20 }, // 00001101000 + { 0x01b0, 11, 21 }, // 00001101100 + { 0x00dc, 11, 22 }, // 00000110111 + { 0x00a0, 11, 23 }, // 00000101000 + { 0x005c, 11, 24 }, // 00000010111 + { 0x0060, 11, 25 }, // 00000011000 + { 0x0194, 12, 26 }, // 000011001010 + { 0x0196, 12, 27 }, // 000011001011 + { 0x0198, 12, 28 }, // 000011001100 + { 0x019a, 12, 29 }, // 000011001101 + { 0x00d0, 12, 30 }, // 000001101000 + { 0x00d2, 12, 31 }, // 000001101001 + { 0x00d4, 12, 32 }, // 000001101010 + { 0x00d6, 12, 33 }, // 000001101011 + { 0x01a4, 12, 34 }, // 000011010010 + { 0x01a6, 12, 35 }, // 000011010011 + { 0x01a8, 12, 36 }, // 000011010100 + { 0x01aa, 12, 37 }, // 000011010101 + { 0x01ac, 12, 38 }, // 000011010110 + { 0x01ae, 12, 39 }, // 000011010111 + { 0x00d8, 12, 40 }, // 000001101100 + { 0x00da, 12, 41 }, // 000001101101 + { 0x01b4, 12, 42 }, // 000011011010 + { 0x01b6, 12, 43 }, // 000011011011 + { 0x00a8, 12, 44 }, // 000001010100 + { 0x00aa, 12, 45 }, // 000001010101 + { 0x00ac, 12, 46 }, // 000001010110 + { 0x00ae, 12, 47 }, // 000001010111 + { 0x00c8, 12, 48 }, // 000001100100 + { 0x00ca, 12, 49 }, // 000001100101 + { 0x00a4, 12, 50 }, // 000001010010 + { 0x00a6, 12, 51 }, // 000001010011 + { 0x0048, 12, 52 }, // 000000100100 + { 0x006e, 12, 53 }, // 000000110111 + { 0x0070, 12, 54 }, // 000000111000 + { 0x004e, 12, 55 }, // 000000100111 + { 0x0050, 12, 56 }, // 000000101000 + { 0x00b0, 12, 57 }, // 000001011000 + { 0x00b2, 12, 58 }, // 000001011001 + { 0x0056, 12, 59 }, // 000000101011 + { 0x0058, 12, 60 }, // 000000101100 + { 0x00b4, 12, 61 }, // 000001011010 + { 0x00cc, 12, 62 }, // 000001100110 + { 0x00ce, 12, 63 }, // 000001100111 + { 0x0078, 10, 64 }, // 0000001111 + { 0x0190, 12, 128 }, // 000011001000 + { 0x0192, 12, 192 }, // 000011001001 + { 0x00b6, 12, 256 }, // 000001011011 + { 0x0066, 12, 320 }, // 000000110011 + { 0x0068, 12, 384 }, // 000000110100 + { 0x006a, 12, 448 }, // 000000110101 + { 0x006c, 13, 512 }, // 0000001101100 + { 0x006d, 13, 576 }, // 0000001101101 + { 0x004a, 13, 640 }, // 0000001001010 + { 0x004b, 13, 704 }, // 0000001001011 + { 0x004c, 13, 768 }, // 0000001001100 + { 0x004d, 13, 832 }, // 0000001001101 + { 0x0072, 13, 896 }, // 0000001110010 + { 0x0073, 13, 960 }, // 0000001110011 + { 0x0074, 13, 1024 }, // 0000001110100 + { 0x0075, 13, 1088 }, // 0000001110101 + { 0x0076, 13, 1152 }, // 0000001110110 + { 0x0077, 13, 1216 }, // 0000001110111 + { 0x0052, 13, 1280 }, // 0000001010010 + { 0x0053, 13, 1344 }, // 0000001010011 + { 0x0054, 13, 1408 }, // 0000001010100 + { 0x0055, 13, 1472 }, // 0000001010101 + { 0x005a, 13, 1536 }, // 0000001011010 + { 0x005b, 13, 1600 }, // 0000001011011 + { 0x0064, 13, 1664 }, // 0000001100100 + { 0x0065, 13, 1728 }, // 0000001100101 + { 0x0020, 11, 1792 }, // 00000001000 + { 0x0030, 11, 1856 }, // 00000001100 + { 0x0034, 11, 1920 }, // 00000001101 + { 0x0024, 12, 1984 }, // 000000010010 + { 0x0026, 12, 2048 }, // 000000010011 + { 0x0028, 12, 2112 }, // 000000010100 + { 0x002a, 12, 2176 }, // 000000010101 + { 0x002c, 12, 2240 }, // 000000010110 + { 0x002e, 12, 2304 }, // 000000010111 + { 0x0038, 12, 2368 }, // 000000011100 + { 0x003a, 12, 2432 }, // 000000011101 + { 0x003c, 12, 2496 }, // 000000011110 + { 0x003e, 12, 2560 }, // 000000011111 + { 0x0000, 0, -1 } // Illegal entry +}; + + + + +// ---------------------------------------- +// SOURCE OF BITS + +#define VLSBUFSIZE 64 + +class MMRDecoder::VLSource : public GPEnabled +{ +protected: + VLSource(GP<ByteStream> &inp); + void init(const bool striped); +public: + // Initializes a bit source on a bytestream + static GP<VLSource> create(GP<ByteStream> &inp, const bool striped); + + // Synchronize on the next stripe + void nextstripe(void); + // Returns a 32 bits integer with at least the + // next sixteen code bits in the high order bits. + inline unsigned int peek(void); + // Ensures that next #peek()# contains at least + // the next 24 code bits. + void preload(void); + // Consumes #n# bits. + void shift(const int n); +private: + GP<ByteStream> ginp; + ByteStream &inp; + unsigned char buffer[ VLSBUFSIZE ]; + unsigned int codeword; + int lowbits; + int bufpos; + int bufmax; + int readmax; +}; + +MMRDecoder::VLSource::VLSource(GP<ByteStream> &xinp) +: ginp(xinp), inp(*ginp), codeword(0), + lowbits(0), bufpos(0), bufmax(0), + readmax(-1) +{} + +void +MMRDecoder::VLSource::init(const bool striped) +{ + if (striped) + readmax = inp.read32(); + lowbits = 32; + preload(); +} + +GP<MMRDecoder::VLSource> +MMRDecoder::VLSource::create(GP<ByteStream> &inp, const bool striped) +{ + VLSource *src=new VLSource(inp); + GP<VLSource> retval=src; + src->init(striped); + return retval; +} + +void +MMRDecoder::VLSource::shift(const int n) +{ + codeword<<=n; + lowbits+=n; + if (lowbits>=16) + preload(); +} + +inline unsigned int +MMRDecoder::VLSource::peek(void) +{ + return codeword; +} + + +void +MMRDecoder::VLSource::nextstripe(void) +{ + while (readmax>0) + { + int size = sizeof(buffer); + if (readmax < size) + size = readmax; + inp.readall(buffer, size); + readmax -= size; + } + bufpos = bufmax = 0; + memset(buffer,0,sizeof(buffer)); + readmax = inp.read32(); + codeword = 0; + lowbits = 32; + preload(); +} + +void +MMRDecoder::VLSource::preload(void) +{ + while (lowbits>=8) + { + if (bufpos >= bufmax) + { + // Refill buffer + bufpos = bufmax = 0; + int size = sizeof(buffer); + if (readmax>=0 && readmax<size) + size = readmax; + if (size>0) + bufmax = inp.read((void*)buffer, size); + readmax -= bufmax; + if (bufmax <= 0) + return; + } + lowbits -= 8; + codeword |= buffer[bufpos++] << lowbits; + } +} + + + +// ---------------------------------------- +// VARIABLE LENGTH CODES + + + +class MMRDecoder::VLTable : public GPEnabled +{ +protected: + VLTable(const VLCode *codes); + void init(const int nbits); +public: + // Construct a VLTable given a codebook with #nbits# long codes. + static GP<VLTable> create(VLCode const * const codes, const int nbits); + + // Reads one symbol from a VLSource + int decode(MMRDecoder::VLSource *src); + + const VLCode *code; + int codewordshift; + unsigned char *index; + GPBuffer<unsigned char> gindex; +}; + +GP<MMRDecoder::VLTable> +MMRDecoder::VLTable::create(VLCode const * const codes, const int nbits) +{ + VLTable *table=new VLTable(codes); + GP<VLTable> retval=table; + table->init(nbits); + return retval; +} + +inline int +MMRDecoder::VLTable::decode(MMRDecoder::VLSource *src) +{ + const VLCode &c = code[ index[ src->peek() >> codewordshift ] ]; + src->shift(c.codelen); + return c.value; +} + +MMRDecoder::VLTable::VLTable(const VLCode *codes) +: code(codes), codewordshift(0), gindex(index,0) +{} + +void +MMRDecoder::VLTable::init(const int nbits) +{ + // count entries + int ncodes = 0; + while (code[ncodes].codelen) + ncodes++; + // check arguments + if (nbits<=1 || nbits>16) + G_THROW(invalid_mmr_data); + if (ncodes>=256) + G_THROW(invalid_mmr_data); + codewordshift = 32 - nbits; + // allocate table + int size = (1<<nbits); + gindex.resize(size); + gindex.set(ncodes); + // process codes + for (int i=0; i<ncodes; i++) { + const int c = code[i].code; + const int b = code[i].codelen; + if(b<=0 || b>nbits) + { + G_THROW(invalid_mmr_data); + } + // fill table entries whose index high bits are code. + int n = c + (1<<(nbits-b)); + while ( --n >= c ) { + if(index[n] != ncodes) + G_THROW( ERR_MSG("MMRDecoder.bad_codebook") ); + index[n] = i; + } + } +} + +// ---------------------------------------- +// MMR DECODER + + + +MMRDecoder::~MMRDecoder() {} + +MMRDecoder::MMRDecoder( const int xwidth, const int xheight ) +: width(xwidth), height(xheight), lineno(0), + striplineno(0), rowsperstrip(0), gline(line,width+8), + glineruns(lineruns,width+4), gprevruns(prevruns,width+4) +{ + gline.clear(); + glineruns.clear(); + gprevruns.clear(); + lineruns[0] = width; + prevruns[0] = width; +} + +void +MMRDecoder::init(GP<ByteStream> gbs, const bool striped) +{ + rowsperstrip = (striped ? gbs->read16() : height); + src = VLSource::create(gbs, striped); + mrtable = VLTable::create(mrcodes, 7); + btable = VLTable::create(bcodes, 13); + wtable = VLTable::create(wcodes, 13); +} + +GP<MMRDecoder> +MMRDecoder::create( GP<ByteStream> gbs, const int width, + const int height, const bool striped ) +{ + MMRDecoder *mmr=new MMRDecoder(width,height); + GP<MMRDecoder> retval=mmr; + mmr->init(gbs,striped); + return retval; +} + +const unsigned short * +MMRDecoder::scanruns(const unsigned short **endptr) +{ + // Check if all lines have been returned + if (lineno >= height) + return 0; + // Check end of stripe + if ( striplineno == rowsperstrip ) + { + striplineno=0; + lineruns[0] = prevruns[0] = width; + src->nextstripe(); + } + // Swap run buffers + unsigned short *pr = lineruns; + unsigned short *xr = prevruns; + prevruns = pr; + lineruns = xr; + // Loop until scanline is complete + bool a0color = false; + int a0,rle,b1; + for(a0=0,rle=0,b1=*pr++;a0 < width;) + { + // Process MMR codes + const int c=mrtable->decode(src); + switch ( c ) + { + /* Pass Mode */ + case P: + { + b1 += *pr++; + rle += b1 - a0; + a0 = b1; + b1 += *pr++; + break; + } + /* Horizontal Mode */ + case H: + { + // First run + VLTable &table1 = *(a0color ? btable : wtable); + int inc; + do { inc=table1.decode(src); a0+=inc; rle+=inc; } while (inc>=64); + *xr = rle; xr++; rle = 0; + // Second run + VLTable &table2 = *(!a0color ? btable : wtable); + do { inc=table2.decode(src); a0+=inc; rle+=inc; } while (inc>=64); + *xr = rle; xr++; rle = 0; + break; + } + /* Vertical Modes */ + case V0: + case VR3: + case VR2: + case VR1: + case VL3: + case VL2: + case VL1: + { + int inc=b1; + switch ( c ) + { + case V0: + inc = b1; + b1 += *pr++; + break; + case VR3: + inc = b1+3; + b1 += *pr++; + break; + case VR2: + inc = b1+2; + b1 += *pr++; + break; + case VR1: + inc = b1+1; + b1 += *pr++; + break; + case VL3: + inc = b1-3; + b1 -= *--pr; + break; + case VL2: + inc = b1-2; + b1 -= *--pr; + break; + case VL1: + inc = b1-1; + b1 -= *--pr; + break; + } + *xr = inc+rle-a0; + xr++; + a0 = inc; + rle = 0; + a0color = !a0color; + break; + } + /* Uncommon modes */ + default: + { + src->preload(); + unsigned int m = src->peek(); + // -- Could be EOFB ``000000000001000000000001'' + // TIFF6 says that all remaining lines are white + if ((m & 0xffffff00) == 0x00100100) + { + lineno = height; + return 0; + } + // -- Could be UNCOMPRESSED ``0000001111'' + // TIFF6 says people should not do this. + // RFC1314 says people should do this. + else if ((m & 0xffc00000) == 0x03c00000) + { +#ifdef MMRDECODER_REFUSES_UNCOMPRESSED + G_THROW( ERR_MSG("MMRDecoder.cant_process") ); +#else + // ---THE-FOLLOWING-CODE-IS-POORLY-TESTED--- + src->shift(10); + while ((m = (src->peek() & 0xfc000000))) + { + if (m == 0x04000000) // 000001 + { + src->shift(6); + if (a0color) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + rle += 5; + a0 += 5; + } + else // 000010 to 111111 + { + src->shift(1); + if (a0color == !(m & 0x80000000)) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + rle++; + a0++; + } + if (a0 > width) + G_THROW(invalid_mmr_data); + } + // Analyze uncompressed termination code. + m = src->peek() & 0xff000000; + src->shift(8); + if ( (m & 0xfe000000) != 0x02000000 ) + G_THROW(invalid_mmr_data); + if (rle) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + if (a0color == !(m & 0x01000000)) + { + *xr = rle; + xr++; + rle = 0; + a0color = !a0color; + } + // Cross fingers and proceed ... + break; +#endif + } + // -- Unknown MMR code. + G_THROW(invalid_mmr_data); + } + } + // Next reference run + for(;b1<=a0 && b1<width;pr+=2) + { + b1 += pr[0]+pr[1]; + } + } + // Final P must be followed by V0 (they say!) + if (rle > 0) + { + if (mrtable->decode(src) != V0) + { + G_THROW(invalid_mmr_data); + } + } + if (rle > 0) + { + *xr = rle; + xr++; + } + // At this point we should have A0 equal to WIDTH + // But there are buggy files around (Kofax!) + // and we are not the CCITT police. + if (a0 > width) + { + while (a0 > width && xr > lineruns) + a0 -= *--xr; + if (a0 < width) + { + *xr = width-a0; + xr++; + } + } + /* Increment and return */ + if (endptr) + *endptr = xr; + xr[0] = 0; + xr[1] = 0; + lineno ++; + striplineno ++; + return lineruns; +} + + + +const unsigned char * +MMRDecoder::scanrle(const bool invert, const unsigned char **endptr) +{ + // Obtain run lengths + const unsigned short *xr = scanruns(); + if (!xr) return 0; + unsigned char *p=line; + // Process inversion + if (invert) + { + if (! *xr) + { + xr++; + }else + { + *p = 0; p++; + } + } + // Encode lenghts using the RLE format + for(int a0=0;a0 < width;) + { + int count = *xr++; + a0 += count; + GBitmap::append_run(p, count); + } + if (endptr) + *endptr = p; + p[0] = 0; + p[1] = 0; + return line; +} + + +#if 0 +const unsigned char * +MMRDecoder::scanline(void) +{ + // Obtain run lengths + const unsigned short *xr = scanruns(); + if (!xr) return 0; + // Allocate data buffer if needed + unsigned char *p = line; + // Decode run lengths + int a0 = 0; + int a0color = 0; + while (a0 < width) + { + int a1 = a0 + *xr++; + while (a0<a1 && a0<width) + line[a0++] = a0color; + a0color = !a0color; + } + return line; +} +#endif + + + + +// ---------------------------------------- +// MAIN DECODING ROUTINE + +bool +MMRDecoder::decode_header( + ByteStream &inp, int &width, int &height, int &invert) +{ + unsigned long int magic = inp.read32(); + if((magic&0xfffffffc) != 0x4d4d5200) + G_THROW( ERR_MSG("MMRDecoder.unrecog_header") ); + invert = ((magic & 0x1) ? 1 : 0); + const bool strip = ((magic & 0x2) ? 1 : 0); + width = inp.read16(); + height = inp.read16(); + if (width<=0 || height<=0) + G_THROW( ERR_MSG("MMRDecoder.bad_header") ); + return strip; +} + +static inline int MAX(int a, int b) { return a>b ? a : b; } +static inline int MIN(int a, int b) { return a<b ? a : b; } + +GP<JB2Image> +MMRDecoder::decode(GP<ByteStream> gbs) +{ + ByteStream &inp=*gbs; + // Read header + int width, height, invert; + const bool striped=decode_header(inp, width, height, invert); + // Prepare image + GP<JB2Image> jimg = JB2Image::create(); + jimg->set_dimension(width, height); + // Choose pertinent blocksize + int blocksize = MIN(500,MAX(64,MAX(width/17,height/22))); + int blocksperline = (width+blocksize-1)/blocksize; + // Prepare decoder + GP<MMRDecoder> gdcd=MMRDecoder::create(gbs, width, height, striped); + MMRDecoder &dcd=*gdcd; + // Loop on JB2 bands + int line = height-1; + while (line >= 0) + { + int bandline = MIN(blocksize-1,line); + GPArray<GBitmap> blocks(0,blocksperline-1); + // Loop on scanlines + for(; bandline >= 0; bandline--,line--) + { + // Decode one scanline + const unsigned short *s = dcd.scanruns(); + if (s) + { + // Loop on blocks + int x = 0; + int b = 0; + int firstx = 0; + bool c = !!invert; + while (x < width) + { + int xend = x + *s++; + while (b<blocksperline) + { + int lastx = MIN(firstx+blocksize,width); + if (c) + { + if (!blocks[b]) + blocks[b] = GBitmap::create(bandline+1, lastx-firstx); + unsigned char *bptr = (*blocks[b])[bandline] - firstx; + int x1 = MAX(x,firstx); + int x2 = MIN(xend,lastx); + while (x1 < x2) + bptr[x1++] = 1; + } + if (xend < lastx) + break; + firstx = lastx; + b ++; + } + x = xend; + c = !c; + } + } + } + // Insert blocks into JB2Image + for (int b=0; b<blocksperline; b++) + { + JB2Shape shape; + shape.bits = blocks[b]; + if (shape.bits) + { + shape.parent = -1; + shape.bits->compress(); + JB2Blit blit; + blit.left = b*blocksize; + blit.bottom = line+1; + blit.shapeno = jimg->add_shape(shape); + jimg->add_blit(blit); + } + } + } + // Return + return jimg; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h new file mode 100644 index 00000000..6516b4cd --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMRDecoder.h @@ -0,0 +1,238 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMRDecoder.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _MMRDECODER_H_ +#define _MMRDECODER_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +#include "GSmartPointer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class JB2Image; + +/** @name MMRDecoder.h + Files #"MMRDecoder.h"# and #"MMRDecoder.cpp"# implement a + CCITT-G4/MMR decoder suitable for use in DjVu. The main + entry point is function \Ref{MMRDecoder::decode}. + + The foreground mask layer of a DjVu file is usually encoded with a + #"Sjbz"# chunk containing JB2 encoded data (cf. \Ref{JB2Image.h}). + Alternatively, the qmask layer may be encoded with a #"Smmr"# + chunk containing a small header followed by MMR encoded data. + This encoding scheme produces significantly larger files. On the + other hand, many scanners a printers talk MMR using very efficient + hardware components. This is the reason behind the introduction + of #"Smmr"# chunks. + + The #Smmr# chunk starts by a header containing the following data: + \begin{verbatim} + BYTE*3 : 'M' 'M' 'R' + BYTE : 0xb000000<s><i> + INT16 : <width> (MSB first) + INT16 : <height> (MSB first) + \end{verbatim} + + The header is followed by the encoded data. Bit 0 of the fourth header + byte (#<i>#) is similar to TIFF's ``min-is-black'' tag. This bit is set + for a reverse video image. The encoded data can be in either ``regular'' + MMR form or ``striped'' MMR form. This is indicated by bit 1 of the + fourth header byte (#<s>#). This bit is set to indicate ``striped'' + data. The ``regular'' data format consists of ordinary MMR encoded data. + The ``striped'' data format consists of one sixteen bit integer (msb + first) containing the number of rows per stripe, followed by data for each + stripe as follows. + \begin{verbatim} + INT16 : <rowsperstripe> (MSB first) + INT32 : <nbytes1> + BYTE*<nbytes1> : <mmrdata1> + INT32 : <nbytes2> + BYTE*<nbytes2> : <mmrdata2> + ... + \end{verbatim} + Static function \Ref{MMRDecoder::decode_header} decodes the header. You + can then create a \Ref{MMRDecoder} object with the flags #inverted# and + #striped# as obtained when decoding the header. One can also decode raw + MMR data by simply initialising a \Ref{MMRDecoder} object with flag + #striped# unset. Each call to \Ref{MMRDecoder::scanruns}, + \Ref{MMRDecoder::scanrle} or \Ref{MMRDecoder::scanline} will then decode a + row of the MMR encoded image. + + Function \Ref{MMRDecoder::decode} is a convenience function for decoding + the contents of a #"Smmr"# chunk. It returns a \Ref{JB2Image} divided + into manageable blocks in order to provide the zooming and panning + features implemented by class \Ref{JB2Image}. + + @memo + CCITT-G4/MMR decoder. + @version + #$Id: MMRDecoder.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @author + Parag Deshmukh <[email protected]> \\ + Leon Bottou <[email protected]> */ +//@{ + + + +#define MMRDECODER_HAS_SCANRUNS 1 +#define MMRDECODER_HAS_SCANRLE 1 + + + +/** Class for G4/MMR decoding. The simplest way to use this class is + the static member function \Ref{MMRDecoder::decode}. This + function internally creates an instance of #MMRDecoder# which + processes the MMR data scanline by scanline. */ +class MMRDecoder : public GPEnabled +{ +protected: + MMRDecoder(const int width, const int height); + void init(GP<ByteStream> gbs, const bool striped=false); +public: + /** Main decoding routine that (a) decodes the header using + #decode_header#, (b) decodes the MMR data using an instance of + #MMRDecoder#, and returns a new \Ref{JB2Image} composed of tiles + whose maximal width and height is derived from the size of the + image. */ + static GP<JB2Image> decode(GP<ByteStream> gbs); + + /// Only decode the header. + static bool decode_header(ByteStream &inp, + int &width, int &height, int &invert); + +public: + /// Non-virtual destructor. + ~MMRDecoder(); + /** Create a MMRDecoder object for decoding an image + of size #width# by #height#. Flag $striped# must be set + if the image is composed of multiple stripes. */ + static GP<MMRDecoder> create(GP<ByteStream> gbs, + const int width, const int height, + const bool striped=false ); + + /** Decodes a scanline and returns a pointer to an array of run lengths. + The returned buffer contains the length of alternative white and black + runs. These run lengths sum to the image width. They are followed by + two zeroes. The position of these two zeroes is stored in the pointer + specified by the optional argument #endptr#. The buffer data should be + processed before calling this function again. */ + const unsigned short *scanruns(const unsigned short **endptr=0); + /** Decodes a scanline and returns a pointer to RLE encoded data. The + buffer contains the length of the runs for the current line encoded as + described in \Ref{PNM and RLE file formats}.) The flag #invert# can be + used to indicate that the MMR data is encoded in reverse video. The RLE + data is followed by two zero bytes. The position of these two zeroes is + stored in the pointer specified by the optional argument #endptr#. The + buffer data should be processed before calling this function again. This + is implemented by calling \Ref{MMRDecoder::scanruns}. */ + const unsigned char *scanrle(const bool invert, + const unsigned char **endptr=0); +#if 0 + /** Decodes a scanline and returns a pointer to an array of #0# or #1# bytes. + Returns a pointer to the scanline buffer containing one byte per pixel. + The buffer data should be processed before calling this function again. + This is implemented by calling \Ref{MMRDecoder::scanruns}. */ + const unsigned char *scanline(); +#endif + private: + int width; + int height; + int lineno; + int striplineno; + int rowsperstrip; + unsigned char *line; + GPBuffer<unsigned char> gline; + unsigned short *lineruns; + GPBuffer<unsigned short> glineruns; + unsigned short *prevruns; + GPBuffer<unsigned short> gprevruns; +public: + class VLSource; + class VLTable; +private: + GP<VLSource> src; + GP<VLTable> mrtable; + GP<VLTable> wtable; + GP<VLTable> btable; + friend class VLSource; + friend class VLTable; +}; + + +//@} + + +// ----------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMX.cpp b/kviewshell/plugins/djvu/libdjvu/MMX.cpp new file mode 100644 index 00000000..58b74177 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMX.cpp @@ -0,0 +1,209 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMX.cpp,v 1.10 2004/05/13 16:50:10 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "MMX.h" +#include <stdio.h> +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +// ---------------------------------------- +// PRINTING MMX REGISTERS (Debug) + + +#if defined(MMX) && defined(DEBUG) +extern "C" void +mmx_show() +{ + /* This function can be called from a debugger + in order to visualize the contents of the MMX registers. */ + int mmregs[16]; + MMXra( movq, mm0, &mmregs[0]); + MMXra( movq, mm1, &mmregs[2]); + MMXra( movq, mm2, &mmregs[4]); + MMXra( movq, mm3, &mmregs[6]); + MMXra( movq, mm4, &mmregs[8]); + MMXra( movq, mm5, &mmregs[10]); + MMXra( movq, mm6, &mmregs[12]); + MMXra( movq, mm7, &mmregs[14]); + MMXemms; + for (int i=0; i<8; i++) + DjVuPrintMessageUTF8("mm%d: %08x%08x\n", i, + mmregs[i+i+1], mmregs[i+i]); + MMXar( movq, &mmregs[0], mm0); + MMXar( movq, &mmregs[2], mm1); + MMXar( movq, &mmregs[4], mm2); + MMXar( movq, &mmregs[6], mm3); + MMXar( movq, &mmregs[8], mm4); + MMXar( movq, &mmregs[10], mm5); + MMXar( movq, &mmregs[12], mm6); + MMXar( movq, &mmregs[14], mm7); +} +#endif + + + +// ---------------------------------------- +// MMX ENABLE/DISABLE + +// Default settings autodetect MMX. +// Use macro DISABLE_MMX to disable MMX by default. + +#if defined(MMX) && !defined(DISABLE_MMX) +int MMXControl::mmxflag = -1; +#else +int MMXControl::mmxflag = 0; +#endif + +int +MMXControl::disable_mmx() +{ + mmxflag = 0; + return mmxflag; +} + +int +MMXControl::enable_mmx() +{ + int cpuflags = 0; +#if defined(MMX) && defined(__GNUC__) && defined(__i386__) + // Detection of MMX for GCC + __asm__ volatile ("pushl %%ebx\n\t" + "pushfl\n\t" + "popl %%ecx\n\t" + "xorl %%edx,%%edx\n\t" + // Check that CPUID exists + "movl %%ecx,%%eax\n\t" + "xorl $0x200000,%%eax\n\t" + "pushl %%eax\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %%eax\n\t" + "xorl %%ecx,%%eax\n\t" + "jz 1f\n\t" + "pushl %%ecx\n\t" + "popfl\n\t" + // Check that CR0:EM is clear + "smsw %%ax\n\t" + "andl $4,%%eax\n\t" + "jnz 1f\n\t" + // Execute CPUID + "movl $1,%%eax\n\t" + "cpuid\n" + // EBX contains magic when -fPIC is on. + "1:\tpopl %%ebx\n\t" + "movl %%edx, %0" + : "=m" (cpuflags) : + : "eax","ecx","edx"); +#endif +#if defined(MMX) && defined(_MSC_VER) && defined(_M_IX86) + // Detection of MMX for MSVC + __asm { pushfd + pop ecx + xor edx,edx + ;// Check that CPUID exists + mov eax,ecx + xor eax,0x200000 + push eax + popfd + pushfd + pop eax + xor eax,ecx + jz fini + push ecx + popfd + ;// Check that CR0:EM is zero + smsw ax + and eax,4 + jnz fini + ;// Execute CPUID + mov eax,1 + _emit 0xf + _emit 0xa2 + fini: + mov cpuflags,edx + ;// MSVC determines clobbered registers by scanning the assembly code. + ;// Since it does not know CPUID, it would not know that EBX is clobbered + ;// without the dummy instruction below... + xor ebx,ebx + } +#endif + mmxflag = !!(cpuflags & 0x800000); + return mmxflag; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/MMX.h b/kviewshell/plugins/djvu/libdjvu/MMX.h new file mode 100644 index 00000000..41ec002f --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/MMX.h @@ -0,0 +1,194 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: MMX.h,v 1.9 2003/12/01 22:57:40 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _MMX_H_ +#define _MMX_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include "DjVuGlobal.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +/** @name MMX.h + Files #"MMX.h"# and #"MMX.cpp"# implement basic routines for + supporting the MMX instructions on x86. Future instruction sets + for other processors may be supported in this file as well. + + Macro #MMX# is defined if the compiler supports the X86-MMX instructions. + It does not mean however that the processor supports the instruction set. + Variable #MMXControl::mmxflag# must be used to decide whether MMX. + instructions can be executed. MMX instructions are entered in the middle + of C++ code using the following macros. Examples can be found in + #"IWTransform.cpp"#. + + \begin{description} + \item[MMXrr( insn, srcreg, dstreg)] + Encode a register to register MMX instruction + (e.g. #paddw# or #punpcklwd#). + \item[MMXar( insn, addr, dstreg )] + Encode a memory to register MMX instruction + (e.g. #moveq# from memory). + \item[MMXra( insn, srcreg, addr )] + Encode a register to memory MMX instruction + (e.g. #moveq# to memory). + \item[MMXir( insn, imm, dstreg )] + Encode a immediate to register MMX instruction + (e.g #psraw#). + \item[MMXemms] + Execute the #EMMS# instruction to reset the FPU state. + \end{description} + + @memo + Essential support for MMX. + @version + #$Id: MMX.h,v 1.9 2003/12/01 22:57:40 leonb Exp $# + @author: + L\'eon Bottou <[email protected]> -- initial implementation */ +//@{ + + +/** MMX Control. + Class #MMXControl# encapsulates a few static functions for + globally enabling or disabling MMX support. */ + +class MMXControl +{ + public: + // MMX DETECTION + /** Detects and enable MMX or similar technologies. This function checks + whether the CPU supports a vectorial instruction set (such as Intel's + MMX) and enables them. Returns a boolean indicating whether such an + instruction set is available. Speedups factors may vary. */ + static int enable_mmx(); + /** Disables MMX or similar technologies. The transforms will then be + performed using the baseline code. */ + static int disable_mmx(); + /** Contains a value greater than zero if the CPU supports vectorial + instructions. A negative value means that you must call \Ref{enable_mmx} + and test the value again. Direct access to this member should only be + used to transfer the instruction flow to the vectorial branch of the + code. Never modify the value of this variable. Use #enable_mmx# or + #disable_mmx# instead. */ + static int mmxflag; // readonly +}; + +//@} + + + + +// ---------------------------------------- +// GCC MMX MACROS + +#ifndef NO_MMX + +#if defined(__GNUC__) && defined(__i386__) +#define MMXemms \ + __asm__ volatile("emms" : : : "memory" ) +#define MMXrr(op,src,dst) \ + __asm__ volatile( #op " %%" #src ",%%" #dst : : : "memory") +#define MMXir(op,imm,dst) \ + __asm__ volatile( #op " %0,%%" #dst : : "i" (imm) : "memory") +#define MMXar(op,addr,dst) \ + __asm__ volatile( #op " %0,%%" #dst : : "m" (*(int*)(addr)) : "memory") +#define MMXra(op,src,addr) \ + __asm__ volatile( #op " %%" #src ",%0" : : "m" (*(int*)(addr)) : "memory") +#define MMX 1 +#endif + + +// ---------------------------------------- +// MSVC MMX MACROS + +#if defined(_MSC_VER) && defined(_M_IX86) +// Compiler option /GM is required +#pragma warning( disable : 4799 ) +#define MMXemms \ + __asm { emms } +#define MMXrr(op,src,dst) \ + __asm { op dst,src } +#define MMXir(op,imm,dst) \ + __asm { op dst,imm } +#define MMXar(op,addr,dst) \ + { register __int64 var=*(__int64*)(addr); __asm { op dst,var } } +#define MMXra(op,src,addr) \ + { register __int64 var; __asm { op [var],src }; *(__int64*)addr = var; } +// Probably not as efficient as GCC macros +#define MMX 1 +#endif + +#endif + +// ----------- + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/Makefile.am b/kviewshell/plugins/djvu/libdjvu/Makefile.am new file mode 100644 index 00000000..4795da0d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) + +kde_module_LTLIBRARIES = libdjvu.la +libdjvu_la_LDFLAGS = -avoid-version $(all_libraries) +libdjvu_la_LIBADD = $(LIBJPEG) +libdjvu_la_SOURCES = Arrays.cpp DjVuDocEditor.cpp DjVuMessageLite.cpp GOS.cpp IW44Image.cpp \ + BSByteStream.cpp DjVuDocument.cpp DjVuNavDir.cpp GPixmap.cpp JB2EncodeCodec.cpp \ + BSEncodeByteStream.cpp DjVuDumpHelper.cpp DjVuPalette.cpp GRect.cpp JB2Image.cpp \ + ByteStream.cpp DjVuErrorList.cpp DjVuPort.cpp GScaler.cpp JPEGDecoder.cpp \ + DataPool.cpp DjVuFileCache.cpp DjVuText.cpp GSmartPointer.cpp MMRDecoder.cpp \ + DjVuFile.cpp DjVuToPS.cpp GString.cpp MMX.cpp DjVmNav.cpp \ + debug.cpp DjVuGlobal.cpp GBitmap.cpp GThreads.cpp UnicodeByteStream.cpp \ + DjVmDir0.cpp DjVuGlobalMemory.cpp GContainer.cpp GUnicode.cpp XMLParser.cpp \ + DjVmDir.cpp DjVuImage.cpp GException.cpp GURL.cpp XMLTags.cpp \ + DjVmDoc.cpp DjVuInfo.cpp GIFFManager.cpp IFFByteStream.cpp ZPCodec.cpp \ + DjVuAnno.cpp DjVuMessage.cpp GMapAreas.cpp IW44EncodeCodec.cpp + +KDE_OPTIONS = nofinal diff --git a/kviewshell/plugins/djvu/libdjvu/README.djvulibre b/kviewshell/plugins/djvu/libdjvu/README.djvulibre new file mode 100644 index 00000000..fb37c8d7 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/README.djvulibre @@ -0,0 +1,85 @@ + + +1- WHAT IS DJVU. +================ + +DjVu (pronounced "d�j� vu") a set of compression technologies, a file format, +and a software platform for the delivery over the Web of digital documents, +scanned documents, and high resolution images. + +DjVu documents download and display extremely quickly, and look exactly the +same on all platforms. DjVu can be seen as superior alternative to PDF and +Postscript for digital documents, to TIFF (and PDF) for scanned documents, to +JPEG for photographs and pictures, and to GIF for large palettized +images. DjVu is the only Web format that is practical for distributing +high-resolution scanned documents in color. No other format comes close. + +Typical DjVu file sizes are as follows: + +- Bitonal scanned documents: + 5 to 30KB per page at 300dpi, + 3 to 10 times smaller than PDF or TIFF. + +- Color scanned documents: + 30 to 100KB per page at 300dpi, + 5 to 10 times smaller than JPEG. + +- Photos: + 2 times smaller than JPEG, + about the same as JPEG-2000. + +- Palettized images: + 2 times smaller than GIF, + up to 10 times if there is text. + +DjVu is used by hundreds of commercial, governmental, and non-commercial web +sites around the world to distribute scanned documents, digital documents, and +high-resolution photos. + +Demos, and general information about DjVu can be found at +http://www.djvuzone.org, or at http://www.lizardtech.com. + +DjVu was originally developed at AT&T Labs-Research. AT&T sold DjVu to +LizardTech Inc. in March 2000. + + + +2- WHAT IS DJVULIBRE? +===================== + +In an effort to promote DjVu as a Web standard, LizardTech's management was +enlightened enough to release the reference implementation of DjVu under the +GNU GPL in October 2000. DjVuLibre (pronounced like the French "d�j� vu +libre"), is an enhanced version of that code maintained by the original +inventors of DjVu. It is compatible with LizardTech's DjVu software v3.5. + +DjVuLibre includes: + +- A standalone DjVu viewer for Unix under X11 (based on the Qt library). + +- A browser plugin that works with most Unix browsers, including: + Netscape-4.x, Netscape-6.x, Mozilla, Galeon, Konqueror, and Opera. + +- A full-fledged wavelet-based compressor for pictures. + +- A simple compressor for bitonal (black and white) scanned pages. + +- A compressor for palettized images (a la GIF). + +- A full set of utilities to manipulate and assemble DjVu images and documents. + +- A set of decoders to convert DjVu to a number of other formats. + +- An up-to-date version of the C++ DjVu Reference Library + +Windows and Mac versions of the viewer/plug-in, as well as commercial versions +of the compressors and OCR engines are available from LizardTech Inc.. The +compressors provided here are slower, produce larger files, and sometimes +lower quality images than the commercial compressors, but they do the job. + +A variety of free web-based conversion services are also available, including +any2djvu.djvuzone.org, bib2web.djvuzone.org, and openlib.djvuzone.org. + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/Template.h b/kviewshell/plugins/djvu/libdjvu/Template.h new file mode 100644 index 00000000..c37935ce --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/Template.h @@ -0,0 +1,258 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: Template.h,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +//T// This is a template for the header files in the +//T// DjVu reference library. It describes the general +//T// conventions as well as the documentation. +//T// Comments prefixed with '//T//' explain the template +//T// features and should be removed. + +#ifndef _TEMPLATE_H_ +#define _TEMPLATE_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +//T// Always include "DjVuGlobal.h" +#include "DjVuGlobal.h" + +//T// Other include files +#include <string.h> +#include "GException.h" + +//T// Begin name space + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +/** @name Template.h + + Files #"Template.h"# and #"Template.cpp"# are not used for anything but + the current programming and documentation standards in the DjVu reference + library. This doc++ comment briefly describes the abstractions defined in + this files. It must mention all the files involved in implementing this + features, as well as references to the main classes \Ref{classname}. + + This comment may contain additional sections as follows: + + {\bf Algorithmic Remarks} --- Comments about the algorithms, their + performance and their limitations. + + {\bf Historical Remarks} --- Comments about the successive revisions of + this file and other anecdotical details. This is where we can amuse the + reader with funny details. + + {\bf ToDo} --- Things that we have been thinking to do but did not + fully implement yet. It should explain how we plan to modify the current + code could be modified to implement these things. People who change this + code thus should avoid jeopardizing these plans. + + {\bf Example} --- It would be cool to demonstrate how these functions + can be used by providing a small segment of C/C++ code. + \begin{verbatim} + ExampleClass toto(3,4); + toto.draw(mywin); + \end{verbatim} + + This main doc++ comment is followed by a few doc++ entries. + \begin{itemize} + \item the "memo" field contains a single line description of the file. + \item the "version" field contains a cvs magic expression. + \item the author fields contains a list of authors and email addresses. + (the #\\# termination breaks the line.) + \end{itemize} + + @memo + Template header file + @version + #$Id: Template.h,v 1.8 2003/11/07 22:08:22 leonb Exp $# + @author: + L\'eon Bottou <[email protected]> -- initial implementation \\ + Andrew Erofeev <[email protected]> -- implemented EXTERNAL_TEMPLATES */ +//@{ +//T// The magic doc++ comment above opens a doc++ context. + + + +//T// Now comes the 'interface part' of the file. +//T// The c++ classes and public functions are defined there. +//T// Doc++ comments must be inserted for all functions +//T// intended to be used by other people. +//T// +//T// Quite often c++ sucks and it is necessary to have public or external symbols +//T// that actually are only there for implementation purposes. +//T// It is good to give a comment but this should not be a doc++ comment +//T// (see class GPool in GContainer.h). All other 'public' and 'protected' +//T// members should have a doc++ comment. There is no need to comment 'private' +//T// members, although a regular comment can be useful (not a doc++ comment). + + + +/** One-line class description. + Long description. There is no need to repeat the class name in the + one-line description. The long description should describe the abstraction + and point the user to the main member functions. An example could be + inserted when this is informative and not redundant with the file + description. Templates should document which member functions are + required for their type argument. The availability of non availabilty of a + copy constructor/copy operator can be specified when appropriate. + See the doc++ documentation for available LaTeX constructs. +*/ + +class ExampleClass +{ +public: + /** Virtual Destructor. */ + ~ExampleClass(); + /** Null Constructor. */ + ExampleClass(); + /** Copy Constructor. */ + ExampleClass(ExampleClass &ref); + /** Copy operator. */ + ExampleClass& operator=(ExampleClass &ref); + /** Example of member function. The first sentence of the member + function description must be a short single line description. + The rest can be more verbose. Excerpts of C or C++ text should + be surrounded by dieze characters (as in #win#). The doc++ #@param# + construct should be used when there is a need for additional details + about the arguments. In that case all the arguments must be documented + with a #@param# directive. + @param win drawing window. + This window must be created with #CreateWindow# and must be visible when + function #draw# is called. + */ + void draw(Window win); +protected: + /** Minimal x-coordinate. */ + int xmin; + /** Maximal x-coordinate. */ + int xmax; +private: + int whatever; + float encode; +}; + + +/** One-line function description. + Long description. Public external functions should be documented + as classes. Note that a family of public external function can be + introduced by a generic entry (see below) */ + +ExampleClass combine_example_classes(const ExampleClass&, + const ExampleClass &b); + + +/** @name Generic entry. + Long description. there is sometimes a need to add documentation + entries for grouping things which are not connected by the C++ + syntax (a family of functions, a family of defines, etc...). + The description starts with a very short name (introduced with #@name#) + followed by a long description. Because of doc++ limitations, + the one-line description must appear after the long description + in a #@memo# entry. + @memo One-line description +*/ + +//T// The following comments should be used when +//T// the preceding generic entry contains sub-entries +//@{ +//T// Sub-entries (both DOC++ and C++) should be declared there. +//@} + + + + + + +//@} +//T// The magic doc++ comment above closes the doc++ file context. +//T// The rest of the file only contains implementation stuff. + +// ------------ CLASSEXAMPLE INLINES +//T// This is where all the inline/template functions should be written +//T// This part of the file is segmented with comments. + +inline void +ClassExample::width() +{ + return xmax-xmin; +} + + + +// ------------ THE END +//T// End name space + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif +//T// Terminates the multiple inclusion #ifndef + + + + + diff --git a/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp new file mode 100644 index 00000000..8d4f0188 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp @@ -0,0 +1,368 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: UnicodeByteStream.cpp,v 1.8 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "UnicodeByteStream.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +UnicodeByteStream::UnicodeByteStream(const UnicodeByteStream &uni) +: bs(uni.bs), buffer(uni.buffer), bufferpos(uni.bufferpos), linesread(0) +{ + startpos=bs->tell(); +} + +UnicodeByteStream::UnicodeByteStream( + GP<ByteStream> ibs,const GStringRep::EncodeType et) +: bs(ibs), bufferpos(0), linesread(0) +{ + buffer=GUTF8String::create(0,0,et); + startpos=bs->tell(); +} + +UnicodeByteStream::~UnicodeByteStream() +{} + +static int +CountLines(const GUTF8String &str) +{ + int retval=0; + static const unsigned long lf='\n'; + for(int pos=0;(pos=str.search(lf,pos)+1)>0;++retval) + EMPTY_LOOP; + return retval; +} + +void +UnicodeByteStream::set_encodetype(const GStringRep::EncodeType et) +{ + seek(startpos,SEEK_SET); + bufferpos=0; + buffer=GUTF8String::create(0,0,et); +} + +void +UnicodeByteStream::set_encoding(const GUTF8String &xencoding) +{ + seek(startpos,SEEK_SET); + bufferpos=0; + buffer=GUTF8String::create(0,0,xencoding); +} + +size_t +UnicodeByteStream::read(void *buf, size_t size) +{ + bufferpos=0; + const int retval=bs->read(buf,size); + if(retval) + { + buffer=GUTF8String::create( + (unsigned char const *)buf,retval,buffer.get_remainder()); + }else + { + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + } + return retval; +} + +size_t +UnicodeByteStream::write(const void *buf, size_t size) +{ + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + return bs->write(buf,size); +} + +long +UnicodeByteStream::tell(void) const +{ + return bs->tell(); +} + +UnicodeByteStream & +UnicodeByteStream::operator=(UnicodeByteStream &uni) +{ + bs=uni.bs; + bufferpos=uni.bufferpos; + buffer=uni.buffer; + return *this; +} + +int +UnicodeByteStream::seek +(long offset, int whence, bool nothrow) +{ + int retval=bs->seek(offset,whence,nothrow); + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); + return retval; +} + +void +UnicodeByteStream::flush(void) +{ + bs->flush(); + bufferpos=0; + buffer=GUTF8String::create(0,0,buffer.get_remainder()); +} + + + +GUTF8String +UnicodeByteStream::gets( + size_t const t,unsigned long const stopat,bool const inclusive) +{ + GUTF8String retval; + unsigned int len=buffer.length()-bufferpos; + if(!len) + { + int i; + char *buf; + static const size_t bufsize=327680; + GPBuffer<char> gbuf(buf,bufsize); + while((i=read(buf,bufsize)>0)) + { + if((len=buffer.length()-bufferpos)) + break; + } + } + if(len) + { + int i=buffer.search((char)stopat,bufferpos); + if(i>=0) + { + if(inclusive) + { + ++i; + } + if(t&&(i>(int)t+bufferpos)) + { + i=t+bufferpos; + } + if(i>bufferpos) + { + retval=buffer.substr(bufferpos,i-bufferpos); + } + bufferpos=i; + linesread+=CountLines(retval); + }else + { + retval=buffer.substr(bufferpos,len); + bufferpos=buffer.length(); + linesread+=CountLines(retval); + retval+=gets(t?(t-(i-bufferpos)):0,stopat,inclusive); + } + } + return retval; +} + +XMLByteStream::XMLByteStream(UnicodeByteStream &uni) +: UnicodeByteStream(uni) {} + +XMLByteStream::XMLByteStream(GP<ByteStream> &ibs) +: UnicodeByteStream(ibs,GStringRep::XOTHER) +{} + +GP<XMLByteStream> +XMLByteStream::create(GP<ByteStream> ibs) +{ + XMLByteStream *xml=new XMLByteStream(ibs); + GP<XMLByteStream> retval=xml; + xml->init(); + return retval; +} + +void +XMLByteStream::init(void) +{ + unsigned char buf[4]; + GP<ByteStream> ibs=bs; + bufferpos=0; + bs->readall(buf,sizeof(buf)); + const unsigned int i=(buf[0]<<8)+buf[1]; + switch(i) + { + case 0x0000: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x003C: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4BE); + break; + } + case 0x3C00: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4_2143); + break; + } + case 0xFEFF: + { + buffer=GUTF8String::create(0,0,GStringRep::XUCS4BE); + startpos+=sizeof(buf); + break; + } + case 0xFFFE: + { + buffer=GUTF8String::create(0,0,GStringRep::XUCS4_2143); + startpos+=sizeof(buf); + break; + } + default: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + } + } + case 0x003C: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x0000: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4_3412); + break; + case 0x003F: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF16BE); + break; + default: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + break; + } + case 0x3C00: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + switch(j) + { + case 0x0000: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUCS4LE); + break; + case 0x3F00: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF16LE); + break; + default: + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + break; + } + break; + } + case 0x4C6F: + { + const unsigned int j=(buf[2]<<8)+buf[3]; + buffer=GUTF8String::create(buf,sizeof(buf), + (j == 0xA794)?(GStringRep::XEBCDIC):(GStringRep::XUTF8)); + break; + } + case 0xFFFE: + { + buffer=GUTF8String::create(buf+2,sizeof(buf)-2,GStringRep::XUTF16LE); + startpos+=2; + break; + } + case 0xFEFF: + { + buffer=GUTF8String::create(buf+2,sizeof(buf)-2,GStringRep::XUTF16BE); + startpos+=2; + break; + } + case 0xEFBB: + { + if(buf[2] == 0xBF) + { + buffer=GUTF8String::create(buf+3,sizeof(buf)-3,GStringRep::XUTF8); + startpos+=3; + }else + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + } + break; + } + case 0x3C3F: + default: + { + buffer=GUTF8String::create(buf,sizeof(buf),GStringRep::XUTF8); + } + } + bs=ibs; +} + +XMLByteStream::~XMLByteStream() +{} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h new file mode 100644 index 00000000..df678ffe --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h @@ -0,0 +1,199 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: UnicodeByteStream.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _UNICODEBYTESTREAM_H_ +#define _UNICODEBYTESTREAM_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + + +/** @name UnicodeByteStream.h + + Files #"UnicodeByteStream.h"# and #"UnicodeByteStream.cpp"# implement a parser for + files structured W3C Extensible Markup Language (XML) 1.0 (Second Edition). + + Class \Ref{UnicodeByteStream} provides a way to read or write XML files. + files. Member functions provide an easy mean to position the underlying + \Ref{ByteStream}. + + {\bf References} --- W3C Extensible Markup Language (XML) 1.0 + (Second Edition) + \URL{http://www.w3.org/TR/2000/REC-xml-20001006.html} + + @memo + XML file parser. + @author + Bill C Riemers <[email protected]> + @version + #$Id: UnicodeByteStream.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# */ +//@{ + +#include "DjVuGlobal.h" +#include "GString.h" +#include "ByteStream.h" + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + + +/** ByteStream interface for an Unicode file. + + Class #UnicodeByteStream# augments the #ByteStream# interface with + functions for navigating Unicode documents. It works in relation + with a ByteStream specified at construction time. + + {\bf Reading an Unicode file} --- You can read an Unicode file by + constructing an #UnicodeByteStream# object attached to the ByteStream + containing the Unicode file. + + {\bf Writing an Unicode file} --- You can write an Unicode file by + constructing an #UnicodeByteStream# object attached to the seekable + ByteStream object that will contain the XML file. + + Writing an XML file requires a seekable ByteStream (see + \Ref{ByteStream::is_seekable}). This is not much of a problem because you + can always create the XML file into a \Ref{MemoryByteStream} and then use + \Ref{ByteStream::copy} to transfer the XML file into a non seekable + ByteStream. */ + +class UnicodeByteStream : public ByteStream +{ +protected: + UnicodeByteStream(const UnicodeByteStream &bs); + UnicodeByteStream(GP<ByteStream> bs, + const GStringRep::EncodeType encodetype=GStringRep::XUTF8); +public: + /** Constructs an UnicodeByteStream object attached to ByteStream #bs#. + Any ByteStream can be used when reading an XML file. Writing + an XML file however requires a seekable ByteStream. */ + static GP<UnicodeByteStream> create(GP<ByteStream> bs, + const GStringRep::EncodeType encodetype=GStringRep::XUTF8) + { return new UnicodeByteStream(bs,encodetype); } + + // --- BYTESTREAM INTERFACE + ~UnicodeByteStream(); + /// Sets the encoding type and seek's to position 0. + void set_encodetype(const GStringRep::EncodeType et=GStringRep::XUTF8); + void set_encoding(const GUTF8String &encoding); + /// Simmular to fgets(), except read aheads effect the tell() position. + virtual GUTF8String gets(size_t const t=0,unsigned long const stopat='\n',bool const inclusive=true); + /// Resets the gets buffering as well as physically seeking. + virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false); + /** Physically reads the specified bytes, and truncate the read ahead buffer. + */ + virtual size_t read(void *buffer, size_t size); + /// Not correctly implimented... + virtual size_t write(const void *buffer, size_t size); + /// tell will tell you the read position, including read ahead for gets()... + virtual long tell(void) const; + /// Does a flush, and clears the read ahead buffer. + virtual void flush(void); + + /// Find out how many lines have been read with gets. + int get_lines_read(void) const { return linesread; } +protected: + /// The real byte stream. + GP<ByteStream> bs; + GUTF8String buffer; + int bufferpos; + int linesread; + long startpos; +private: + // Cancel C++ default stuff + UnicodeByteStream & operator=(UnicodeByteStream &); +}; + + +class XMLByteStream : public UnicodeByteStream +{ +protected: + XMLByteStream(GP<ByteStream> &bs); + XMLByteStream(UnicodeByteStream &bs); + void init(void); +public: + static GP<XMLByteStream> create(GP<ByteStream> bs); + static GP<XMLByteStream> create(UnicodeByteStream &bs); + // --- BYTESTREAM INTERFACE + ~XMLByteStream(); +}; + +inline GP<XMLByteStream> +XMLByteStream::create(UnicodeByteStream &bs) +{ + return new XMLByteStream(bs); +} + +//@} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + diff --git a/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp b/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp new file mode 100644 index 00000000..b1d9f469 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLParser.cpp @@ -0,0 +1,1128 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLParser.cpp,v 1.10 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "XMLParser.h" +#include "XMLTags.h" +#include "ByteStream.h" +#include "GOS.h" +#include "DjVuDocument.h" +#include "DjVuText.h" +#include "DjVuAnno.h" +#include "DjVuFile.h" +#include "DjVuImage.h" +#include "debug.h" +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +static const char mimetype[]="image/x.djvu"; +static const char bodytag[]="BODY"; +static const char areatag[]="AREA"; +static const char maptag[]="MAP"; +static const char objecttag[]="OBJECT"; +static const char paramtag[]="PARAM"; +static const char wordtag[]="WORD"; +static const char linetag[]="LINE"; +static const char paragraphtag[]="PARAGRAPH"; +static const char regiontag[]="REGION"; +static const char pagecolumntag[]="PAGECOLUMN"; +static const char hiddentexttag[]="HIDDENTEXT"; +static const char metadatatag[]="METADATA"; + +class lt_XMLParser::Impl : public lt_XMLParser +{ +public: + Impl(void); + virtual ~Impl(); + /// Parse the specified bytestream. + virtual void parse(const GP<ByteStream> &bs); + /// Parse the specified tags - this one does all the work + virtual void parse(const lt_XMLTags &tags); + /// write to disk. + virtual void save(void); + /// erase. + virtual void empty(void); +protected: + GP<DjVuFile> get_file(const GURL &url,GUTF8String page); + + void parse_anno(const int width, const int height, + const lt_XMLTags &GObject, + GMap<GUTF8String,GP<lt_XMLTags> > &Maps, DjVuFile &dfile); + + void parse_text(const int width, const int height, + const lt_XMLTags &GObject, DjVuFile &dfile); + + void parse_meta(const lt_XMLTags &GObject, DjVuFile &dfile); + + void ChangeAnno( const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeInfo(DjVuFile &dfile,const int dpi,const double gamma); + + void ChangeText( const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeMeta( DjVuFile &dfile, const lt_XMLTags &map); + + void ChangeTextOCR( const GUTF8String &value, + const int width, const int height, + const GP<DjVuFile> &dfile); + + // we may want to make these list of modified file static so + // they only needed to be loaded and saved once. + + GMap<GUTF8String,GP<DjVuFile> > m_files; + GMap<GUTF8String,GP<DjVuDocument> > m_docs; + + GURL m_codebase; + GCriticalSection xmlparser_lock; +}; + +static GP<ByteStream> +OCRcallback( + void * const xarg, + lt_XMLParser::mapOCRcallback * const xcallback, + const GUTF8String &value=GUTF8String(), + const GP<DjVuImage> &image=0 ); + +static inline GP<ByteStream> +OCRcallback(const GUTF8String &value, const GP<DjVuImage> &image) +{ + return OCRcallback(0,0,value,image); +} + +lt_XMLParser::lt_XMLParser() {} +lt_XMLParser::~lt_XMLParser() {} +lt_XMLParser::Impl::Impl() {} +lt_XMLParser::Impl::~Impl() {} + +GP<lt_XMLParser> +lt_XMLParser::create(void) +{ + return new lt_XMLParser::Impl; +} + +// helper function for args +static void +intList(GUTF8String coords, GList<int> &retval) +{ + int pos=0; + while(coords.length()) + { + int epos; + unsigned long i=coords.toLong(pos,epos,10); + if(epos>=0) + { + retval.append(i); + const int n=coords.nextNonSpace(epos); + if(coords[n] != ',') + break; + pos=n+1; + } + } +} + +void +lt_XMLParser::Impl::empty(void) +{ + GCriticalSectionLock lock(&xmlparser_lock); + m_files.empty(); + m_docs.empty(); +} + +void +lt_XMLParser::Impl::save(void) +{ + GCriticalSectionLock lock(&xmlparser_lock); + for(GPosition pos=m_docs;pos;++pos) + { + const GP<DjVuDocument> doc(m_docs[pos]); + const GURL url=doc->get_init_url(); + + DEBUG_MSG("Saving "<<(const char *)url<<" with new text and annotations\n"); + const bool bundle=doc->is_bundled()||(doc->get_doc_type()==DjVuDocument::SINGLE_PAGE); + doc->save_as(url,bundle); + } + empty(); +} + +void +lt_XMLParser::Impl::parse(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> tags(lt_XMLTags::create(bs)); + parse(*tags); +} + +static const GMap<GUTF8String,GMapArea::BorderType> & +BorderTypeMap(void) +{ + static GMap<GUTF8String,GMapArea::BorderType> typeMap; + if (! typeMap.size()) + { + typeMap["none"]=GMapArea::NO_BORDER; + typeMap["xor"]=GMapArea::XOR_BORDER; + typeMap["solid"]=GMapArea::SOLID_BORDER; + typeMap["default"]=GMapArea::SOLID_BORDER; + typeMap["shadowout"]=GMapArea::SHADOW_OUT_BORDER; + typeMap["shadowin"]=GMapArea::SHADOW_IN_BORDER; + typeMap["etchedin"]=GMapArea::SHADOW_EIN_BORDER; + typeMap["etchedout"]=GMapArea::SHADOW_EOUT_BORDER; + } + return typeMap; +} + +static unsigned long +convertToColor(const GUTF8String &s) +{ + unsigned long retval=0; + if(s.length()) + { + int endpos; + if(s[0] == '#') + { + retval=s.substr(1,-1).toULong(0,endpos,16); + } + if(endpos < 0) + { + G_THROW( (ERR_MSG("XMLAnno.bad_color") "\t")+s ); + } + } + return retval; +} + +void +lt_XMLParser::Impl::ChangeInfo(DjVuFile &dfile,const int dpi,const double gamma) +{ + GP<DjVuInfo> info; + if(dpi >= 5 && dpi <= 4800) + { + dfile.resume_decode(true); + if(dfile.info && (dpi != dfile.info->dpi) ) + { + info=new DjVuInfo(*dfile.info); + info->dpi=dpi; + } + } + if(gamma >= 0.1 && gamma <= 5.0) + { + dfile.resume_decode(true); + if(dfile.info && (gamma != dfile.info->gamma) ) + { + if(!info) + info=new DjVuInfo(*dfile.info); + info->gamma=gamma; + } + } + if(info) + { + dfile.change_info(info); + } +} + +void +lt_XMLParser::Impl::ChangeAnno( + const int width, const int height, + DjVuFile &dfile, + const lt_XMLTags &map ) +{ + dfile.resume_decode(true); + const GP<DjVuInfo> info(dfile.info); + const GP<DjVuAnno> ganno(DjVuAnno::create()); + DjVuAnno &anno=*ganno; + GPosition map_pos; + map_pos=map.contains(areatag); + if(dfile.contains_anno()) + { + GP<ByteStream> annobs=dfile.get_merged_anno(); + if(annobs) + { + anno.decode(annobs); + if(anno.ant && info) + { + anno.ant->map_areas.empty(); + } + } +// dfile.remove_anno(); + } + if(info && map_pos) + { + const int h=info->height; + const int w=info->width; + double ws=1.0; + double hs=1.0; + if(width && width != w) + { + ws=((double)w)/((double)width); + } + if(height && height != h) + { + hs=((double)h)/((double)height); + } + if(!anno.ant) + { + anno.ant=DjVuANT::create(); + } + GPList<GMapArea> &map_areas=anno.ant->map_areas; + map_areas.empty(); + GPList<lt_XMLTags> gareas=map[map_pos]; + for(GPosition pos=gareas;pos;++pos) + { + if(gareas[pos]) + { + lt_XMLTags &areas=*(gareas[pos]); + GMap<GUTF8String,GUTF8String> args(areas.get_args()); + GList<int> coords; + // ****************************************************** + // Parse the coords attribute: first read the raw data into + // a list, then scale the x, y data into another list. For + // circles, you also get a radius element with (looks like an x + // with no matching y). + // ****************************************************** + { + GPosition coords_pos=args.contains("coords"); + if(coords_pos) + { + GList<int> raw_coords; + intList(args[coords_pos],raw_coords); + for(GPosition raw_pos=raw_coords;raw_pos;++raw_pos) + { + const int r=raw_coords[raw_pos]; + const int x=(int)(ws*(double)r+0.5); + coords.append(x); + int y=h-1; + if(! ++raw_pos) + { + y-=(int)(hs*(double)r+0.5); + }else + { + y-=(int)(hs*(double)raw_coords[raw_pos]+0.5); + } + coords.append(y); +// DjVuPrintMessage("Coords (%d,%d)\n",x,y); + } + } + } + GUTF8String shape; + { + GPosition shape_pos=args.contains("shape"); + if(shape_pos) + { + shape=args[shape_pos]; + } + } + GP<GMapArea> a; + if(shape == "default") + { + GRect rect(0,0,w,h); + a=GMapRect::create(rect); + }else if(!shape.length() || shape == "rect") + { + int xx[4]; + int i=0; + for(GPosition rect_pos=coords;(rect_pos)&&(i<4);++rect_pos,++i) + { + xx[i]=coords[rect_pos]; + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_rect") ); + } + int xmin,xmax; + if(xx[0]>xx[2]) + { + xmax=xx[0]; + xmin=xx[2]; + }else + { + xmin=xx[0]; + xmax=xx[2]; + } + int ymin,ymax; + if(xx[1]>xx[3]) + { + ymax=xx[1]; + ymin=xx[3]; + }else + { + ymin=xx[1]; + ymax=xx[3]; + } + GRect rect(xmin,ymin,xmax-xmin,ymax-ymin); + a=GMapRect::create(rect); + }else if(shape == "circle") + { + int xx[4]; + int i=0; + GPosition rect_pos=coords.lastpos(); + if(rect_pos) + { + coords.append(coords[rect_pos]); + for(rect_pos=coords;(rect_pos)&&(i<4);++rect_pos) + { + xx[i++]=coords[rect_pos]; + } + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_circle") ); + } + int x=xx[0],y=xx[1],rx=xx[2],ry=(h-xx[3])-1; + GRect rect(x-rx,y-ry,2*rx,2*ry); + a=GMapOval::create(rect); + }else if(shape == "oval") + { + int xx[4]; + int i=0; + for(GPosition rect_pos=coords;(rect_pos)&&(i<4);++rect_pos,++i) + { + xx[i]=coords[rect_pos]; + } + if(i!=4) + { + G_THROW( ERR_MSG("XMLAnno.bad_oval") ); + } + int xmin,xmax; + if(xx[0]>xx[2]) + { + xmax=xx[0]; + xmin=xx[2]; + }else + { + xmin=xx[0]; + xmax=xx[2]; + } + int ymin,ymax; + if(xx[1]>xx[3]) + { + ymax=xx[1]; + ymin=xx[3]; + }else + { + ymin=xx[1]; + ymax=xx[3]; + } + GRect rect(xmin,ymin,xmax-xmin,ymax-ymin); + a=GMapOval::create(rect); + }else if(shape == "poly") + { + GP<GMapPoly> p=GMapPoly::create(); + for(GPosition poly_pos=coords;poly_pos;++poly_pos) + { + int x=coords[poly_pos]; + if(! ++poly_pos) + break; + int y=coords[poly_pos]; + p->add_vertex(x,y); + } + p->close_poly(); + a=p; + }else + { + G_THROW( ( ERR_MSG("XMLAnno.unknown_shape") "\t")+shape ); + } + if(a) + { + GPosition pos; + if((pos=args.contains("href"))) + { + a->url=args[pos]; + } + if((pos=args.contains("target"))) + { + a->target=args[pos]; + } + if((pos=args.contains("alt"))) + { + a->comment=args[pos]; + } + if((pos=args.contains("bordertype"))) + { + GUTF8String b=args[pos]; + static const GMap<GUTF8String,GMapArea::BorderType> typeMap=BorderTypeMap(); + if((pos=typeMap.contains(b))) + { + a->border_type=typeMap[pos]; + }else + { + G_THROW( (ERR_MSG("XMLAnno.unknown_border") "\t")+b ); + } + } + a->border_always_visible=!!args.contains("visible"); + if((pos=args.contains("bordercolor"))) + { + a->border_color=convertToColor(args[pos]); + } + if((pos=args.contains("highlight"))) + { + a->hilite_color=convertToColor(args[pos]); + } + if((pos=args.contains("border"))) + { + a->border_width=args[pos].toInt(); //atoi(args[pos]); + } + map_areas.append(a); + } + } + } + } + dfile.set_modified(true); + dfile.anno=ByteStream::create(); + anno.encode(dfile.anno); +} + +GP<DjVuFile> +lt_XMLParser::Impl::get_file(const GURL &url,GUTF8String id) +{ + GP<DjVuFile> dfile; + GP<DjVuDocument> doc; + GCriticalSectionLock lock(&xmlparser_lock); + { + GPosition pos=m_docs.contains(url.get_string()); + if(pos) + { + doc=m_docs[pos]; + }else + { + doc=DjVuDocument::create_wait(url); + if(! doc->wait_for_complete_init()) + { + G_THROW(( ERR_MSG("XMLAnno.fail_init") "\t")+url.get_string() ); + } + m_docs[url.get_string()]=doc; + } + if(id.is_int()) + { + const int xpage=id.toInt(); //atoi((char const *)page); + if(xpage>0) + id=doc->page_to_id(xpage-1); + }else if(!id.length()) + { + id=doc->page_to_id(0); + } + } + const GURL fileurl(doc->id_to_url(id)); + GPosition dpos(m_files.contains(fileurl.get_string())); + if(!dpos) + { + if(!doc->get_id_list().contains(id)) + { + G_THROW( ERR_MSG("XMLAnno.bad_page") ); + } + dfile=doc->get_djvu_file(id,false); + if(!dfile) + { + G_THROW( ERR_MSG("XMLAnno.bad_page") ); + } + m_files[fileurl.get_string()]=dfile; + }else + { + dfile=m_files[dpos]; + } + return dfile; +} + +void +lt_XMLParser::Impl::parse(const lt_XMLTags &tags) +{ + const GPList<lt_XMLTags> Body(tags.get_Tags(bodytag)); + GPosition pos=Body; + + if(!pos || (pos != Body.lastpos())) + { + G_THROW( ERR_MSG("XMLAnno.extra_body") ); + } + const GP<lt_XMLTags> GBody(Body[pos]); + if(!GBody) + { + G_THROW( ERR_MSG("XMLAnno.no_body") ); + } + + GMap<GUTF8String,GP<lt_XMLTags> > Maps; + lt_XMLTags::get_Maps(maptag,"name",Body,Maps); + + const GPList<lt_XMLTags> Objects(GBody->get_Tags(objecttag)); + lt_XMLTags::get_Maps(maptag,"name",Objects,Maps); + + for(GPosition Objpos=Objects;Objpos;++Objpos) + { + lt_XMLTags &GObject=*Objects[Objpos]; + // Map of attributes to value (e.g. "width" --> "500") + const GMap<GUTF8String,GUTF8String> &args=GObject.get_args(); + GURL codebase; + { + DEBUG_MSG("Setting up codebase... m_codebase = " << m_codebase << "\n"); + GPosition codebasePos=args.contains("codebase"); + // If user specified a codebase attribute, assume it is correct (absolute URL): + // the GURL constructor will throw an exception if it isn't + if(codebasePos) + { + codebase=GURL::UTF8(args[codebasePos]); + }else if (m_codebase.is_dir()) + { + codebase=m_codebase; + }else + { + codebase=GURL::Filename::UTF8(GOS::cwd()); + } + DEBUG_MSG("codebase = " << codebase << "\n"); + } + // the data attribute specifies the input file. This can be + // either an absolute URL (starts with file:/) or a relative + // URL (for now, just a path and file name). If it's absolute, + // our GURL will adequately wrap it. If it's relative, we need + // to use the codebase attribute to form an absolute URL first. + GPosition datapos=args.contains("data"); + if(datapos) + { + bool isDjVuType=false; + GPosition typePos(args.contains("type")); + if(typePos) + { + if(args[typePos] != mimetype) + { +// DjVuPrintErrorUTF8("Ignoring %s Object tag\n",mimetype); + continue; + } + isDjVuType=true; + } + const GURL url=GURL::UTF8(args[datapos],(args[datapos][0] == '/')?codebase.base():codebase); + int width; + { + GPosition widthPos=args.contains("width"); + width=(widthPos)?args[widthPos].toInt():0; + } + int height; + { + GPosition heightPos=args.contains("height"); + height=(heightPos)?args[heightPos].toInt():0; + } + GUTF8String gamma; + GUTF8String dpi; + GUTF8String page; + GUTF8String do_ocr; + { + GPosition paramPos(GObject.contains(paramtag)); + if(paramPos) + { + const GPList<lt_XMLTags> Params(GObject[paramPos]); + for(GPosition loc=Params;loc;++loc) + { + const GMap<GUTF8String,GUTF8String> &pargs=Params[loc]->get_args(); + GPosition namepos=pargs.contains("name"); + if(namepos) + { + GPosition valuepos=pargs.contains("value"); + if(valuepos) + { + const GUTF8String name=pargs[namepos].downcase(); + const GUTF8String &value=pargs[valuepos]; + if(name == "flags") + { + GMap<GUTF8String,GUTF8String> args; + lt_XMLTags::ParseValues(value,args,true); + if(args.contains("page")) + { + page=args["page"]; + } + if(args.contains("dpi")) + { + dpi=args["dpi"]; + } + if(args.contains("gamma")) + { + gamma=args["gamma"]; + } + if(args.contains("ocr")) + { + do_ocr=args["ocr"]; + } + }else if(name == "page") + { + page=value; + }else if(name == "dpi") + { + dpi=value; + }else if(name == "gamma") + { + gamma=value; + }else if(name == "ocr") + { + do_ocr=value; + } + } + } + } + } + } + const GP<DjVuFile> dfile(get_file(url,page)); + if(dpi.is_int() || gamma.is_float()) + { + int pos=0; + ChangeInfo(*dfile,dpi.toInt(),gamma.toDouble(pos,pos)); + } + parse_anno(width,height,GObject,Maps,*dfile); + parse_meta(GObject,*dfile); + parse_text(width,height,GObject,*dfile); + ChangeTextOCR(do_ocr,width,height,dfile); + } + } +} + +void +lt_XMLParser::Impl::parse_anno( + const int width, + const int height, + const lt_XMLTags &GObject, + GMap<GUTF8String,GP<lt_XMLTags> > &Maps, + DjVuFile &dfile ) +{ + GP<lt_XMLTags> map; + { + GPosition usemappos=GObject.get_args().contains("usemap"); + if(usemappos) + { + const GUTF8String mapname(GObject.get_args()[usemappos]); + GPosition mappos=Maps.contains(mapname); + if(!mappos) + { + G_THROW((ERR_MSG("XMLAnno.map_find") "\t")+mapname ); + }else + { + map=Maps[mappos]; + } + } + } + if(map) + { + ChangeAnno(width,height,dfile,*map); + } +} + +#ifdef max +#undef max +#endif +template<class TYPE> +static inline TYPE max(TYPE a,TYPE b) { return (a>b)?a:b; } +#ifdef min +#undef min +#endif +template<class TYPE> +static inline TYPE min(TYPE a,TYPE b) { return (a<b)?a:b; } + +// used to build the zone tree +// true is returned if the GRect is known for this object, +// and false, if the rectangle's size is just the parent size. +static bool +make_child_layer( + DjVuTXT::Zone &parent, + const lt_XMLTags &tag, ByteStream &bs, + const int height, const double ws, const double hs) +{ + bool retval=true; + // the plugin thinks there are only Pages, Lines and Words + // so we don't make Paragraphs, Regions and Columns zones + // if we did the plugin is not able to search the text but + // DjVuToText writes out all the text anyway + DjVuTXT::Zone *self_ptr; + char sepchar; + const GUTF8String name(tag.get_name()); + if(name == wordtag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::WORD; + sepchar=' '; + }else if(name == linetag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::LINE; + sepchar=DjVuTXT::end_of_line; + }else if(name == paragraphtag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::PARAGRAPH; + sepchar=DjVuTXT::end_of_paragraph; + }else if(name == regiontag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::REGION; + sepchar=DjVuTXT::end_of_region; + }else if(name == pagecolumntag) + { + self_ptr=parent.append_child(); + self_ptr->ztype = DjVuTXT::COLUMN; + sepchar=DjVuTXT::end_of_column; + }else + { + self_ptr = &parent; + self_ptr->ztype = DjVuTXT::PAGE; + sepchar=0; + } + DjVuTXT::Zone &self = *self_ptr; + self.text_start = bs.tell(); + int &xmin=self.rect.xmin, &ymin=self.rect.ymin, + &xmax=self.rect.xmax, &ymax=self.rect.ymax; + GRect default_rect; + default_rect.xmin=max(parent.rect.xmax,parent.rect.xmin); + default_rect.xmax=min(parent.rect.xmax,parent.rect.xmin); + default_rect.ymin=max(parent.rect.ymax,parent.rect.ymin); + default_rect.ymax=min(parent.rect.ymax,parent.rect.ymin); + // Now if there are coordinates, use those. + GPosition pos(tag.get_args().contains("coords")); + if(pos) + { + GList<int> rectArgs; + intList(tag.get_args()[pos], rectArgs); + if((pos=rectArgs)) + { + xmin=(int)(ws*(double)rectArgs[pos]); + if(++pos) + { + ymin=(height-1)-(int)(hs*(double)rectArgs[pos]); + if(++pos) + { + xmax=(int)(ws*(double)rectArgs[pos]); + if(++pos) + { + ymax=(height-1)-(int)(hs*(double)rectArgs[pos]); + if(xmin>xmax) // Make sure xmin is really minimum + { + const int t=xmin; + xmin=xmax; + xmax=t; + } + if(ymin>ymax) // Make sure ymin is really minimum + { + const int t=ymin; + ymin=ymax; + ymax=t; + } + } + } + } + } + } + if(self.ztype == DjVuTXT::WORD) + { + if(! pos) + { + self.rect=default_rect; + retval=false; + } + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else if(pos) + { + pos=tag.get_content(); + if(pos) + { + for(pos=tag.get_content(); pos; ++pos) + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + make_child_layer(self, *t, bs, height,ws,hs); + } + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else + { + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + } + }else + { + self.rect=default_rect; + if((pos=tag.get_content())) + { + do + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + const GRect save_rect(self.rect); + self.rect=default_rect; + if(retval=make_child_layer(self, *t, bs, height,ws,hs)) + { + xmin=min(save_rect.xmin,xmin); + xmax=max(save_rect.xmax,xmax); + ymin=min(save_rect.ymin,ymin); + ymax=max(save_rect.ymax,ymax); + }else + { + // If the child doesn't have coordinates, we need to use a box + // at least as big as the parent's coordinates. + xmin=min(save_rect.xmin,default_rect.xmax); + xmax=max(save_rect.xmax,default_rect.xmin); + ymin=min(save_rect.ymin,default_rect.ymax); + ymax=max(save_rect.ymax,default_rect.ymin); + for(; pos; ++pos) + { + const GP<lt_XMLTags> t(tag.get_content()[pos].tag); + make_child_layer(self, *t, bs, height,ws,hs); + } + break; + } + } while(++pos); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + }else + { + const GUTF8String raw(tag.get_raw().fromEscaped()); + const int i=raw.nextNonSpace(0); + bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i)); + if(sepchar) + bs.write8(sepchar); + self.text_length = bs.tell() - self.text_start; + } + } + parent.rect.xmin=min(xmin,parent.rect.xmin); + parent.rect.ymin=min(ymin,parent.rect.ymin); + parent.rect.xmax=max(xmax,parent.rect.xmax); + parent.rect.ymax=max(ymax,parent.rect.ymax); + if(xmin>xmax) + { + const int t=xmin; + xmin=xmax; + xmax=t; + } + if(ymin>ymax) + { + const int t=ymin; + ymin=ymax; + ymax=t; + } +// DjVuPrintMessage("(%d,%d)(%d,%d)<<<\\%o>>>\n", +// xmin,ymin,xmax,ymax, sepchar); + return retval; +} + +void +lt_XMLParser::Impl::ChangeTextOCR( + const GUTF8String &value, + const int width, + const int height, + const GP<DjVuFile> &dfile) +{ + if(value.length() && value.downcase() != "false") + { + const GP<ByteStream> bs=OCRcallback(value,DjVuImage::create(dfile)); + if( bs && bs->size() ) + { + const GP<lt_XMLTags> tags(lt_XMLTags::create(bs)); + ChangeText(width,height,*dfile,*tags); + } + } +} + +void +lt_XMLParser::Impl::ChangeMeta( + DjVuFile &dfile, const lt_XMLTags &tags ) +{ + dfile.resume_decode(true); + GP<ByteStream> gbs(ByteStream::create()); + tags.write(*gbs,false); + gbs->seek(0L); + GUTF8String raw(gbs->getAsUTF8()); + if(raw.length()) + { + //GUTF8String gs="<"+(metadatatag+(">"+raw))+"</"+metadatatag+">\n"); + dfile.change_meta(raw+"\n"); + }else + { + dfile.change_meta(GUTF8String()); + } +} + +void +lt_XMLParser::Impl::ChangeText( + const int width, const int height, + DjVuFile &dfile, const lt_XMLTags &tags ) +{ + dfile.resume_decode(true); + + GP<DjVuText> text = DjVuText::create(); + GP<DjVuTXT> txt = text->txt = DjVuTXT::create(); + + // to store the new text + GP<ByteStream> textbs = ByteStream::create(); + + GP<DjVuInfo> info=(dfile.info); + if(info) + { + const int h=info->height; + const int w=info->width; + txt->page_zone.text_start = 0; + DjVuTXT::Zone &parent=txt->page_zone; + parent.rect.xmin=0; + parent.rect.ymin=0; + parent.rect.ymax=h; + parent.rect.xmax=w; + double ws=1.0; + if(width && width != w) + { + ws=((double)w)/((double)width); + } + double hs=1.0; + if(height && height != h) + { + hs=((double)h)/((double)height); + } + make_child_layer(parent, tags, *textbs, h, ws,hs); + textbs->write8(0); + long len = textbs->tell(); + txt->page_zone.text_length = len; + textbs->seek(0,SEEK_SET); + textbs->read(txt->textUTF8.getbuf(len), len); + + dfile.change_text(txt,false); + } +} + +void +lt_XMLParser::Impl::parse_text( + const int width, + const int height, + const lt_XMLTags &GObject, + DjVuFile &dfile ) +{ + GPosition textPos = GObject.contains(hiddentexttag); + if(textPos) + { + // loop through the hidden text - there should only be one + // if there are more ??only the last one will be saved?? + GPList<lt_XMLTags> textTags = GObject[textPos]; + GPosition pos = textTags; + ChangeText(width,height,dfile,*textTags[pos]); + } +} + +void +lt_XMLParser::Impl::parse_meta( + const lt_XMLTags &GObject, + DjVuFile &dfile ) +{ + GPosition metaPos = GObject.contains(metadatatag); + if(metaPos) + { + // loop through the hidden text - there should only be one + // if there are more ??only the last one will be saved?? + GPList<lt_XMLTags> metaTags = GObject[metaPos]; + GPosition pos = metaTags; + ChangeMeta(dfile,*metaTags[pos]); + } +} + +static GP<ByteStream> +OCRcallback( + void * const xarg, + lt_XMLParser::mapOCRcallback * const xcallback, + const GUTF8String &value, + const GP<DjVuImage> &image ) +{ + GP<ByteStream> retval; + static void *arg=0; + static lt_XMLParser::mapOCRcallback *callback=0; + if(image) + { + if(callback) + retval=callback(arg,value,image); + }else + { + arg=xarg; + callback=xcallback; + } + return retval; +} + +void +lt_XMLParser::setOCRcallback( + void * const arg, + mapOCRcallback * const callback) +{ + ::OCRcallback(arg,callback); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/XMLParser.h b/kviewshell/plugins/djvu/libdjvu/XMLParser.h new file mode 100644 index 00000000..08b6d508 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLParser.h @@ -0,0 +1,123 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLParser.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _LT_XMLPARSER__ +#define _LT_XMLPARSER__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "GContainer.h" +#include "GURL.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; +class lt_XMLTags; +class lt_XMLContents; +class DjVuFile; +class DjVuDocument; +class DjVuImage; +class GBitmap; + +// this is the base class for using XML to change DjVu Docs. + +class lt_XMLParser : public GPEnabled +{ +public: + class Impl; + typedef GP<ByteStream> mapOCRcallback( + void *,const GUTF8String &value,const GP<DjVuImage> &); +protected: + lt_XMLParser(void); + virtual ~lt_XMLParser(); +public: + static GP<lt_XMLParser> create(void); + /// Parse the specified bytestream. + virtual void parse(const GP<ByteStream> &bs) = 0; + /// Parse the specified tags - this one does all the work + virtual void parse(const lt_XMLTags &tags) = 0; + /// write to disk. + virtual void save(void) = 0; + /// erase. + virtual void empty(void) = 0; + + // helper function for args + static void setOCRcallback( + void * const arg,mapOCRcallback * const ); +}; + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* _LT_XMLPARSER__ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp b/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp new file mode 100644 index 00000000..2511a585 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLTags.cpp @@ -0,0 +1,417 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLTags.cpp,v 1.12 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "XMLTags.h" +#include "UnicodeByteStream.h" +#include <ctype.h> +#if HAS_WCTYPE +#include <wctype.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +lt_XMLContents::lt_XMLContents(void) {} + +lt_XMLContents::lt_XMLContents(GP<lt_XMLTags> t) +{ + tag=t; +} + +static GUTF8String +getargn(char const tag[], char const *&t) +{ + char const *s; + for(s=tag;isspace(*s);s++); + for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&((*t)!='=')&&!isspace(*t);++t); + return GUTF8String(s,t-s); +} + +static GUTF8String +getargv(char const tag[], char const *&t) +{ + GUTF8String retval; + if(tag && tag[0] == '=') + { + char const *s=t=tag+1; + if((*t == '"')||(*t == '\47')) + { + char const q=*(t++); + for(s++;(*t)&&((*t)!=q)&&((*t)!='>');++t); + retval=GUTF8String(s,t-s); + if (t[0] == q) + { + ++t; + } + }else + { + for(t=s;(*t)&&((*t)!='/')&&((*t)!='>')&&!isspace(*t);++t); + retval=GUTF8String(s,t-s); + } + }else + { + t=tag; + } + return retval; +} + +static GUTF8String +tagtoname(char const tag[],char const *&t) +{ + char const *s; + for(s=tag;isspace(*s);s++); + for(t=s;(*t)&&((*t)!='>')&&((*t)!='/')&&!isspace(*t);++t); + return GUTF8String(s,t-s); +} + +static inline GUTF8String +tagtoname(char const tag[]) +{ + char const *t; + return tagtoname(tag,t); +} + +static inline bool +isspaces(const GUTF8String &raw) +{ + return (raw.nextNonSpace() == (int)raw.length()); +} + +void +lt_XMLTags::ParseValues(char const *t, GMap<GUTF8String,GUTF8String> &args,bool downcase) +{ + GUTF8String argn; + char const *tt; + while((argn=getargn(t,tt)).length()) + { + if(downcase) + argn=argn.downcase(); + args[argn]=getargv(tt,t).fromEscaped(); + } +} + +lt_XMLTags::~lt_XMLTags() {} + +lt_XMLTags::lt_XMLTags(void) : startline(0) {} + +lt_XMLTags::lt_XMLTags(const char n[]) : startline(0) +{ + char const *t; + name=tagtoname(n,t); + ParseValues(t,args); +} + +void +lt_XMLTags::init(const GP<ByteStream> &bs) +{ + GP<XMLByteStream> gxmlbs=XMLByteStream::create(bs); + init(*gxmlbs); +} + +void +lt_XMLTags::init(const GURL &url) +{ + const GP<ByteStream> bs=ByteStream::create(url,"rb"); + init(bs); +} + +void +lt_XMLTags::init(XMLByteStream &xmlbs) +{ + if(!get_count()) + { + G_THROW( ERR_MSG("XMLTags.no_GP") ); + } + GPList<lt_XMLTags> level; + GUTF8String tag,raw(xmlbs.gets(0,'<',false)); + int linesread=xmlbs.get_lines_read(); + if(!isspaces(raw)) + { + G_THROW( (ERR_MSG("XMLTags.raw_string") "\t")+raw); + } + GUTF8String encoding; + for(int len;(len=(tag=xmlbs.gets(0,'>',true)).length());) + { + if(tag[len-1] != '>') + { + G_THROW((ERR_MSG("XMLTags.bad_tag") "\t")+tag); + } + switch(tag[1]) + { + case '?': + { + while(len < 4 || tag.substr(len-2,len) != "?>") + { + GUTF8String cont(xmlbs.gets(0,'>',true)); + if(!cont.length()) + { + G_THROW( (ERR_MSG("XMLTags.bad_PI") "\t")+tag); + } + len=((tag+=cont).length()); + } + char const *n; + GUTF8String xtag = tag.substr(2,-1); + GUTF8String xname = tagtoname(xtag,n); + if(xname.downcase() == "xml") + { + ParseValues(n,args); + for(GPosition pos=args;pos;++pos) + { + if(args.key(pos) == "encoding") + { + const GUTF8String e=args[pos].upcase(); + if(e != encoding) + { + xmlbs.set_encoding((encoding=e)); + } + } + } + } + break; + } + case '!': + { + if(tag[2] == '-' && tag[3] == '-') + { + while((len < 7) || + (tag.substr(len-3,-1) != "-->")) + { + GUTF8String cont(xmlbs.gets(0,'>',true)); + if(!cont.length()) + { + GUTF8String mesg; + mesg.format( ERR_MSG("XMLTags.bad_comment") "\t%s",(const char *)tag); + G_THROW(mesg); + } + len=((tag+=cont).length()); + } + } + break; + } + case '/': + { + GUTF8String xname=tagtoname(tag.substr(2,-1)); + GPosition last=level.lastpos(); + if(last) + { + if(level[last]->name != xname) + { + G_THROW( (ERR_MSG("XMLTags.unmatched_end") "\t") + +level[last]->name+("\t"+GUTF8String(level[last]->get_Line())) + +("\t"+xname)+("\t"+GUTF8String(linesread+1))); + } + level.del(last); + }else + { + G_THROW( ERR_MSG("XMLTags.bad_form") ); + } + break; + } + default: + { + GPosition last=level.lastpos(); + GP<lt_XMLTags> t; + if(last) + { + t=new lt_XMLTags(tag.substr(1,len-1)); + level[last]->addtag(t); + if(tag[len-2] != '/') + { + level.append(t); + } + }else if(tag[len-2] != '/') + { + char const *n; + GUTF8String xtag = tag.substr(1,-1); + name=tagtoname(xtag, n); + ParseValues(n,args); + t=this; + level.append(t); + }else + { + G_THROW( ERR_MSG("XMLTags.no_body") ); + } + t->set_Line(linesread+1); + break; + } + } + if((raw=xmlbs.gets(0,'<',false))[0]) + { + linesread=xmlbs.get_lines_read(); + GPosition last=level.lastpos(); + if(last) + { + level[last]->addraw(raw); + }else if(!isspaces(raw)) + { + G_THROW(( ERR_MSG("XMLTags.raw_string") "\t")+raw); + } + } + } +} + +GPList<lt_XMLTags> +lt_XMLTags::get_Tags(char const tagname[]) const +{ + GPosition pos=allTags.contains(tagname); + GPList<lt_XMLTags> retval; + return (pos?allTags[pos]:retval); +} + +void +lt_XMLTags::get_Maps(char const tagname[], + char const argn[], + GPList<lt_XMLTags> list, + GMap<GUTF8String, GP<lt_XMLTags> > &map) +{ + for(GPosition pos=list;pos;++pos) + { + GP<lt_XMLTags> &tag=list[pos]; + if(tag) + { + GPosition loc; + if((loc=tag->contains(tagname))) + { + GPList<lt_XMLTags> maps=(GPList<lt_XMLTags> &)((*tag)[loc]); + for(GPosition mloc=maps;mloc;++mloc) + { + GP<lt_XMLTags> gtag=maps[mloc]; + if(gtag) + { + GMap<GUTF8String,GUTF8String> &args=gtag->args; + GPosition gpos; + if((gpos=args.contains(argn))) + { + map[args[gpos]]=gtag; + } + } + } + } + } + } +} + +void +lt_XMLTags::write(ByteStream &bs,bool const top) const +{ + if(name.length()) + { + GUTF8String tag="<"+name; + for(GPosition pos=args;pos;++pos) + { + tag+=GUTF8String(' ')+args.key(pos)+GUTF8String("=\42")+args[pos].toEscaped()+GUTF8String("\42"); + } + GPosition tags=content; + if(tags||raw.length()) + { + tag+=">"; + bs.writall((const char *)tag,tag.length()); + tag="</"+name+">"; + if(raw.length()) + { + bs.writestring(raw); + } + for(;tags;++tags) + { + content[tags].write(bs); + } + }else if(!raw.length()) + { + tag+="/>"; + } + bs.writall((const char *)tag,tag.length()); + } + if(top) + { + bs.writall("\n",1); + } +} + +void +lt_XMLContents::write(ByteStream &bs) const +{ + if(tag) + { + tag->write(bs,false); + } + if(raw.length()) + { + bs.writestring(raw); + } +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/XMLTags.h b/kviewshell/plugins/djvu/libdjvu/XMLTags.h new file mode 100644 index 00000000..027e629b --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/XMLTags.h @@ -0,0 +1,242 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: XMLTags.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _LT_XMLTAGS__ +#define _LT_XMLTAGS__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// This is purely Lizardtech stuff. + +#include "GContainer.h" +#include "GString.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class lt_XMLContents; +class DjVuFile; +class DjVuDocument; +class ByteStream; +class XMLByteStream; +class GURL; + +class lt_XMLTags : public GPEnabled +{ +protected: + lt_XMLTags(); + lt_XMLTags(const char n[]); + +public: + /// Empty creator. + static GP<lt_XMLTags> create(void) { return new lt_XMLTags; } + /// Default the specified tag. + static GP<lt_XMLTags> create(const char n[]) { return new lt_XMLTags(n); } + /// Initialize from the specified URL. + void init(const GURL & url); + /// Create from the specified URL. + static GP<lt_XMLTags> create(const GURL &url); + /// Initialize from the specified bytestream. + void init(const GP<ByteStream> &bs); + /// Create from the specified bytestream. + static GP<lt_XMLTags> create(const GP<ByteStream> &bs); + /// Initialize from an XMLByteStream. + void init(XMLByteStream &xmlbs); + /// Create from an XML bytestream. + static GP<lt_XMLTags> create(XMLByteStream &xmlbs); + /// Non-virtual destructor. + ~lt_XMLTags(); + + inline int get_Line(void) const; + inline const GUTF8String& get_raw(void) const; + inline const GUTF8String& get_name(void) const; + inline const GList<lt_XMLContents>& get_content(void) const; + inline const GMap<GUTF8String,GUTF8String>& get_args(void) const; + inline const GMap<GUTF8String,GPList<lt_XMLTags> >& get_allTags(void) const; + + GPList<lt_XMLTags> get_Tags(char const tagname[]) const; + inline void set_Line(const int xstartline) { startline=xstartline; } + + inline void addtag(GP<lt_XMLTags> x); + inline void addraw(GUTF8String raw); + inline GPosition contains(GUTF8String name) const; + inline const GPList<lt_XMLTags> & operator [] (const GUTF8String name) const; + inline const GPList<lt_XMLTags> & operator [] (const GPosition &pos) const; + static void ParseValues(char const *t, GMap<GUTF8String,GUTF8String> &args,bool downcase=true); + static void get_Maps(char const tagname[],char const argn[], + GPList<lt_XMLTags> list, GMap<GUTF8String, GP<lt_XMLTags> > &map); + void write(ByteStream &bs,bool const top=true) const; + +protected: + GUTF8String name; + GMap<GUTF8String,GUTF8String> args; + GList<lt_XMLContents> content; + GUTF8String raw; + GMap<GUTF8String,GPList<lt_XMLTags> > allTags; + int startline; +}; + +class lt_XMLContents +{ +public: + lt_XMLContents(void); + lt_XMLContents(GP<lt_XMLTags> tag); + GP<lt_XMLTags> tag; + GUTF8String raw; + void write(ByteStream &bs) const; +}; + +inline GP<lt_XMLTags> +lt_XMLTags::create(const GURL &url) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(url); + return retval; +} + +inline GP<lt_XMLTags> +lt_XMLTags::create(const GP<ByteStream> &bs) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(bs); + return retval; +} + +inline GP<lt_XMLTags> +lt_XMLTags::create(XMLByteStream &xmlbs) +{ + const GP<lt_XMLTags> retval(new lt_XMLTags); + retval->init(xmlbs); + return retval; +} + +/// Non-virtual destructor. +inline void +lt_XMLTags::addtag (GP<lt_XMLTags> x) +{ + content.append(lt_XMLContents(x)); + allTags[x->name].append(x); +} + +inline void +lt_XMLTags::addraw (GUTF8String r) +{ + GPosition pos=content; + if(pos) + { + content[pos].raw+=r; + }else + { + raw+=r; + } +} + +inline int +lt_XMLTags::get_Line(void) const +{ return startline; } + +inline const GUTF8String & +lt_XMLTags::get_name(void) const { return name; } + +inline const GUTF8String & +lt_XMLTags::get_raw(void) const { return raw; } + +inline const GList<lt_XMLContents> & +lt_XMLTags::get_content(void) const { return content; } + +inline const GMap<GUTF8String,GUTF8String> & +lt_XMLTags::get_args(void) const { return args; } + +inline const GMap<GUTF8String,GPList<lt_XMLTags> > & +lt_XMLTags::get_allTags(void) const { return allTags; } + +inline GPosition +lt_XMLTags::contains(GUTF8String name) const +{ + return allTags.contains(name); +} + +inline const GPList<lt_XMLTags> & +lt_XMLTags::operator [] (const GUTF8String name) const +{ + return allTags[name]; +} + +inline const GPList<lt_XMLTags> & +lt_XMLTags::operator [] (const GPosition &pos) const +{ + return allTags[pos]; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif /* _LT_XMLTAGS__ */ + + diff --git a/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp b/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp new file mode 100644 index 00000000..89461872 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp @@ -0,0 +1,1292 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ZPCodec.cpp,v 1.10 2004/08/06 14:49:34 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "ZPCodec.h" +#include "ByteStream.h" +#include "GException.h" +#include <stdlib.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +//////////////////////////////////////////////////////////////// +// CODER SPECIFICATION +//////////////////////////////////////////////////////////////// + + +#ifndef ZPCODER +#ifndef ZCODER +#define ZPCODER +#endif +#endif +#ifdef ZCODER + +// The ZCODER option is provided for documentation purposes only. The ZCODER +// might come dangerously close to U.S. patent 5059976 (Mitsubishi). This is +// why we always use the ZPCODER, although it usually produces 1% larger files. +#warning "The ZCODER may infringe non-LizardTech patent(s)." +#warning "You should use the ZPCODER instead." +#endif + + +//////////////////////////////////////////////////////////////// +// ZP CODER DEFAULT ADAPTATION TABLE +//////////////////////////////////////////////////////////////// + + +// See ZPCodec::ZPCodec to see how this +// default table is modified when not +// using the DjVu compatibility mode. + + +static ZPCodec::Table default_ztable[256] = +{ +#ifdef ZPCODER + /* This table has been designed for the ZPCoder + * by running the following command in file 'zptable.sn': + * (fast-crude (steady-mat 0.0035 0.0002) 260))) + */ + { 0x8000, 0x0000, 84, 145 }, /* 000: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 3, 4 }, /* 001: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 4, 3 }, /* 002: p=0.500000 ( 0, 0) */ + { 0x6bbd, 0x10a5, 5, 1 }, /* 003: p=0.465226 ( 0, 0) */ + { 0x6bbd, 0x10a5, 6, 2 }, /* 004: p=0.465226 ( 0, 0) */ + { 0x5d45, 0x1f28, 7, 3 }, /* 005: p=0.430708 ( 0, 0) */ + { 0x5d45, 0x1f28, 8, 4 }, /* 006: p=0.430708 ( 0, 0) */ + { 0x51b9, 0x2bd3, 9, 5 }, /* 007: p=0.396718 ( 0, 0) */ + { 0x51b9, 0x2bd3, 10, 6 }, /* 008: p=0.396718 ( 0, 0) */ + { 0x4813, 0x36e3, 11, 7 }, /* 009: p=0.363535 ( 0, 0) */ + { 0x4813, 0x36e3, 12, 8 }, /* 010: p=0.363535 ( 0, 0) */ + { 0x3fd5, 0x408c, 13, 9 }, /* 011: p=0.331418 ( 0, 0) */ + { 0x3fd5, 0x408c, 14, 10 }, /* 012: p=0.331418 ( 0, 0) */ + { 0x38b1, 0x48fd, 15, 11 }, /* 013: p=0.300585 ( 0, 0) */ + { 0x38b1, 0x48fd, 16, 12 }, /* 014: p=0.300585 ( 0, 0) */ + { 0x3275, 0x505d, 17, 13 }, /* 015: p=0.271213 ( 0, 0) */ + { 0x3275, 0x505d, 18, 14 }, /* 016: p=0.271213 ( 0, 0) */ + { 0x2cfd, 0x56d0, 19, 15 }, /* 017: p=0.243438 ( 0, 0) */ + { 0x2cfd, 0x56d0, 20, 16 }, /* 018: p=0.243438 ( 0, 0) */ + { 0x2825, 0x5c71, 21, 17 }, /* 019: p=0.217391 ( 0, 0) */ + { 0x2825, 0x5c71, 22, 18 }, /* 020: p=0.217391 ( 0, 0) */ + { 0x23ab, 0x615b, 23, 19 }, /* 021: p=0.193150 ( 0, 0) */ + { 0x23ab, 0x615b, 24, 20 }, /* 022: p=0.193150 ( 0, 0) */ + { 0x1f87, 0x65a5, 25, 21 }, /* 023: p=0.170728 ( 0, 0) */ + { 0x1f87, 0x65a5, 26, 22 }, /* 024: p=0.170728 ( 0, 0) */ + { 0x1bbb, 0x6962, 27, 23 }, /* 025: p=0.150158 ( 0, 0) */ + { 0x1bbb, 0x6962, 28, 24 }, /* 026: p=0.150158 ( 0, 0) */ + { 0x1845, 0x6ca2, 29, 25 }, /* 027: p=0.131418 ( 0, 0) */ + { 0x1845, 0x6ca2, 30, 26 }, /* 028: p=0.131418 ( 0, 0) */ + { 0x1523, 0x6f74, 31, 27 }, /* 029: p=0.114460 ( 0, 0) */ + { 0x1523, 0x6f74, 32, 28 }, /* 030: p=0.114460 ( 0, 0) */ + { 0x1253, 0x71e6, 33, 29 }, /* 031: p=0.099230 ( 0, 0) */ + { 0x1253, 0x71e6, 34, 30 }, /* 032: p=0.099230 ( 0, 0) */ + { 0x0fcf, 0x7404, 35, 31 }, /* 033: p=0.085611 ( 0, 0) */ + { 0x0fcf, 0x7404, 36, 32 }, /* 034: p=0.085611 ( 0, 0) */ + { 0x0d95, 0x75d6, 37, 33 }, /* 035: p=0.073550 ( 0, 0) */ + { 0x0d95, 0x75d6, 38, 34 }, /* 036: p=0.073550 ( 0, 0) */ + { 0x0b9d, 0x7768, 39, 35 }, /* 037: p=0.062888 ( 0, 0) */ + { 0x0b9d, 0x7768, 40, 36 }, /* 038: p=0.062888 ( 0, 0) */ + { 0x09e3, 0x78c2, 41, 37 }, /* 039: p=0.053539 ( 0, 0) */ + { 0x09e3, 0x78c2, 42, 38 }, /* 040: p=0.053539 ( 0, 0) */ + { 0x0861, 0x79ea, 43, 39 }, /* 041: p=0.045365 ( 0, 0) */ + { 0x0861, 0x79ea, 44, 40 }, /* 042: p=0.045365 ( 0, 0) */ + { 0x0711, 0x7ae7, 45, 41 }, /* 043: p=0.038272 ( 0, 0) */ + { 0x0711, 0x7ae7, 46, 42 }, /* 044: p=0.038272 ( 0, 0) */ + { 0x05f1, 0x7bbe, 47, 43 }, /* 045: p=0.032174 ( 0, 0) */ + { 0x05f1, 0x7bbe, 48, 44 }, /* 046: p=0.032174 ( 0, 0) */ + { 0x04f9, 0x7c75, 49, 45 }, /* 047: p=0.026928 ( 0, 0) */ + { 0x04f9, 0x7c75, 50, 46 }, /* 048: p=0.026928 ( 0, 0) */ + { 0x0425, 0x7d0f, 51, 47 }, /* 049: p=0.022444 ( 0, 0) */ + { 0x0425, 0x7d0f, 52, 48 }, /* 050: p=0.022444 ( 0, 0) */ + { 0x0371, 0x7d91, 53, 49 }, /* 051: p=0.018636 ( 0, 0) */ + { 0x0371, 0x7d91, 54, 50 }, /* 052: p=0.018636 ( 0, 0) */ + { 0x02d9, 0x7dfe, 55, 51 }, /* 053: p=0.015421 ( 0, 0) */ + { 0x02d9, 0x7dfe, 56, 52 }, /* 054: p=0.015421 ( 0, 0) */ + { 0x0259, 0x7e5a, 57, 53 }, /* 055: p=0.012713 ( 0, 0) */ + { 0x0259, 0x7e5a, 58, 54 }, /* 056: p=0.012713 ( 0, 0) */ + { 0x01ed, 0x7ea6, 59, 55 }, /* 057: p=0.010419 ( 0, 0) */ + { 0x01ed, 0x7ea6, 60, 56 }, /* 058: p=0.010419 ( 0, 0) */ + { 0x0193, 0x7ee6, 61, 57 }, /* 059: p=0.008525 ( 0, 0) */ + { 0x0193, 0x7ee6, 62, 58 }, /* 060: p=0.008525 ( 0, 0) */ + { 0x0149, 0x7f1a, 63, 59 }, /* 061: p=0.006959 ( 0, 0) */ + { 0x0149, 0x7f1a, 64, 60 }, /* 062: p=0.006959 ( 0, 0) */ + { 0x010b, 0x7f45, 65, 61 }, /* 063: p=0.005648 ( 0, 0) */ + { 0x010b, 0x7f45, 66, 62 }, /* 064: p=0.005648 ( 0, 0) */ + { 0x00d5, 0x7f6b, 67, 63 }, /* 065: p=0.004506 ( 0, 0) */ + { 0x00d5, 0x7f6b, 68, 64 }, /* 066: p=0.004506 ( 0, 0) */ + { 0x00a5, 0x7f8d, 69, 65 }, /* 067: p=0.003480 ( 0, 0) */ + { 0x00a5, 0x7f8d, 70, 66 }, /* 068: p=0.003480 ( 0, 0) */ + { 0x007b, 0x7faa, 71, 67 }, /* 069: p=0.002602 ( 0, 0) */ + { 0x007b, 0x7faa, 72, 68 }, /* 070: p=0.002602 ( 0, 0) */ + { 0x0057, 0x7fc3, 73, 69 }, /* 071: p=0.001843 ( 0, 0) */ + { 0x0057, 0x7fc3, 74, 70 }, /* 072: p=0.001843 ( 0, 0) */ + { 0x003b, 0x7fd7, 75, 71 }, /* 073: p=0.001248 ( 0, 0) */ + { 0x003b, 0x7fd7, 76, 72 }, /* 074: p=0.001248 ( 0, 0) */ + { 0x0023, 0x7fe7, 77, 73 }, /* 075: p=0.000749 ( 0, 0) */ + { 0x0023, 0x7fe7, 78, 74 }, /* 076: p=0.000749 ( 0, 0) */ + { 0x0013, 0x7ff2, 79, 75 }, /* 077: p=0.000402 ( 0, 0) */ + { 0x0013, 0x7ff2, 80, 76 }, /* 078: p=0.000402 ( 0, 0) */ + { 0x0007, 0x7ffa, 81, 77 }, /* 079: p=0.000153 ( 0, 0) */ + { 0x0007, 0x7ffa, 82, 78 }, /* 080: p=0.000153 ( 0, 0) */ + { 0x0001, 0x7fff, 81, 79 }, /* 081: p=0.000027 ( 0, 0) */ + { 0x0001, 0x7fff, 82, 80 }, /* 082: p=0.000027 ( 0, 0) */ + { 0x5695, 0x0000, 9, 85 }, /* 083: p=0.411764 ( 2, 3) */ + { 0x24ee, 0x0000, 86, 226 }, /* 084: p=0.199988 ( 1, 0) */ + { 0x8000, 0x0000, 5, 6 }, /* 085: p=0.500000 ( 3, 3) */ + { 0x0d30, 0x0000, 88, 176 }, /* 086: p=0.071422 ( 4, 0) */ + { 0x481a, 0x0000, 89, 143 }, /* 087: p=0.363634 ( 1, 2) */ + { 0x0481, 0x0000, 90, 138 }, /* 088: p=0.024388 ( 13, 0) */ + { 0x3579, 0x0000, 91, 141 }, /* 089: p=0.285711 ( 1, 3) */ + { 0x017a, 0x0000, 92, 112 }, /* 090: p=0.007999 ( 41, 0) */ + { 0x24ef, 0x0000, 93, 135 }, /* 091: p=0.199997 ( 1, 5) */ + { 0x007b, 0x0000, 94, 104 }, /* 092: p=0.002611 ( 127, 0) */ + { 0x1978, 0x0000, 95, 133 }, /* 093: p=0.137929 ( 1, 8) */ + { 0x0028, 0x0000, 96, 100 }, /* 094: p=0.000849 ( 392, 0) */ + { 0x10ca, 0x0000, 97, 129 }, /* 095: p=0.090907 ( 1, 13) */ + { 0x000d, 0x0000, 82, 98 }, /* 096: p=0.000276 ( 1208, 0) */ + { 0x0b5d, 0x0000, 99, 127 }, /* 097: p=0.061537 ( 1, 20) */ + { 0x0034, 0x0000, 76, 72 }, /* 098: p=0.001102 ( 1208, 1) */ + { 0x078a, 0x0000, 101, 125 }, /* 099: p=0.040815 ( 1, 31) */ + { 0x00a0, 0x0000, 70, 102 }, /* 100: p=0.003387 ( 392, 1) */ + { 0x050f, 0x0000, 103, 123 }, /* 101: p=0.027397 ( 1, 47) */ + { 0x0117, 0x0000, 66, 60 }, /* 102: p=0.005912 ( 392, 2) */ + { 0x0358, 0x0000, 105, 121 }, /* 103: p=0.018099 ( 1, 72) */ + { 0x01ea, 0x0000, 106, 110 }, /* 104: p=0.010362 ( 127, 1) */ + { 0x0234, 0x0000, 107, 119 }, /* 105: p=0.011940 ( 1, 110) */ + { 0x0144, 0x0000, 66, 108 }, /* 106: p=0.006849 ( 193, 1) */ + { 0x0173, 0x0000, 109, 117 }, /* 107: p=0.007858 ( 1, 168) */ + { 0x0234, 0x0000, 60, 54 }, /* 108: p=0.011925 ( 193, 2) */ + { 0x00f5, 0x0000, 111, 115 }, /* 109: p=0.005175 ( 1, 256) */ + { 0x0353, 0x0000, 56, 48 }, /* 110: p=0.017995 ( 127, 2) */ + { 0x00a1, 0x0000, 69, 113 }, /* 111: p=0.003413 ( 1, 389) */ + { 0x05c5, 0x0000, 114, 134 }, /* 112: p=0.031249 ( 41, 1) */ + { 0x011a, 0x0000, 65, 59 }, /* 113: p=0.005957 ( 2, 389) */ + { 0x03cf, 0x0000, 116, 132 }, /* 114: p=0.020618 ( 63, 1) */ + { 0x01aa, 0x0000, 61, 55 }, /* 115: p=0.009020 ( 2, 256) */ + { 0x0285, 0x0000, 118, 130 }, /* 116: p=0.013652 ( 96, 1) */ + { 0x0286, 0x0000, 57, 51 }, /* 117: p=0.013672 ( 2, 168) */ + { 0x01ab, 0x0000, 120, 128 }, /* 118: p=0.009029 ( 146, 1) */ + { 0x03d3, 0x0000, 53, 47 }, /* 119: p=0.020710 ( 2, 110) */ + { 0x011a, 0x0000, 122, 126 }, /* 120: p=0.005961 ( 222, 1) */ + { 0x05c5, 0x0000, 49, 41 }, /* 121: p=0.031250 ( 2, 72) */ + { 0x00ba, 0x0000, 124, 62 }, /* 122: p=0.003925 ( 338, 1) */ + { 0x08ad, 0x0000, 43, 37 }, /* 123: p=0.046979 ( 2, 47) */ + { 0x007a, 0x0000, 72, 66 }, /* 124: p=0.002586 ( 514, 1) */ + { 0x0ccc, 0x0000, 39, 31 }, /* 125: p=0.069306 ( 2, 31) */ + { 0x01eb, 0x0000, 60, 54 }, /* 126: p=0.010386 ( 222, 2) */ + { 0x1302, 0x0000, 33, 25 }, /* 127: p=0.102940 ( 2, 20) */ + { 0x02e6, 0x0000, 56, 50 }, /* 128: p=0.015695 ( 146, 2) */ + { 0x1b81, 0x0000, 29, 131 }, /* 129: p=0.148935 ( 2, 13) */ + { 0x045e, 0x0000, 52, 46 }, /* 130: p=0.023648 ( 96, 2) */ + { 0x24ef, 0x0000, 23, 17 }, /* 131: p=0.199999 ( 3, 13) */ + { 0x0690, 0x0000, 48, 40 }, /* 132: p=0.035533 ( 63, 2) */ + { 0x2865, 0x0000, 23, 15 }, /* 133: p=0.218748 ( 2, 8) */ + { 0x09de, 0x0000, 42, 136 }, /* 134: p=0.053434 ( 41, 2) */ + { 0x3987, 0x0000, 137, 7 }, /* 135: p=0.304346 ( 2, 5) */ + { 0x0dc8, 0x0000, 38, 32 }, /* 136: p=0.074626 ( 41, 3) */ + { 0x2c99, 0x0000, 21, 139 }, /* 137: p=0.241378 ( 2, 7) */ + { 0x10ca, 0x0000, 140, 172 }, /* 138: p=0.090907 ( 13, 1) */ + { 0x3b5f, 0x0000, 15, 9 }, /* 139: p=0.312499 ( 3, 7) */ + { 0x0b5d, 0x0000, 142, 170 }, /* 140: p=0.061537 ( 20, 1) */ + { 0x5695, 0x0000, 9, 85 }, /* 141: p=0.411764 ( 2, 3) */ + { 0x078a, 0x0000, 144, 168 }, /* 142: p=0.040815 ( 31, 1) */ + { 0x8000, 0x0000, 141, 248 }, /* 143: p=0.500000 ( 2, 2) */ + { 0x050f, 0x0000, 146, 166 }, /* 144: p=0.027397 ( 47, 1) */ + { 0x24ee, 0x0000, 147, 247 }, /* 145: p=0.199988 ( 0, 1) */ + { 0x0358, 0x0000, 148, 164 }, /* 146: p=0.018099 ( 72, 1) */ + { 0x0d30, 0x0000, 149, 197 }, /* 147: p=0.071422 ( 0, 4) */ + { 0x0234, 0x0000, 150, 162 }, /* 148: p=0.011940 ( 110, 1) */ + { 0x0481, 0x0000, 151, 95 }, /* 149: p=0.024388 ( 0, 13) */ + { 0x0173, 0x0000, 152, 160 }, /* 150: p=0.007858 ( 168, 1) */ + { 0x017a, 0x0000, 153, 173 }, /* 151: p=0.007999 ( 0, 41) */ + { 0x00f5, 0x0000, 154, 158 }, /* 152: p=0.005175 ( 256, 1) */ + { 0x007b, 0x0000, 155, 165 }, /* 153: p=0.002611 ( 0, 127) */ + { 0x00a1, 0x0000, 70, 156 }, /* 154: p=0.003413 ( 389, 1) */ + { 0x0028, 0x0000, 157, 161 }, /* 155: p=0.000849 ( 0, 392) */ + { 0x011a, 0x0000, 66, 60 }, /* 156: p=0.005957 ( 389, 2) */ + { 0x000d, 0x0000, 81, 159 }, /* 157: p=0.000276 ( 0, 1208) */ + { 0x01aa, 0x0000, 62, 56 }, /* 158: p=0.009020 ( 256, 2) */ + { 0x0034, 0x0000, 75, 71 }, /* 159: p=0.001102 ( 1, 1208) */ + { 0x0286, 0x0000, 58, 52 }, /* 160: p=0.013672 ( 168, 2) */ + { 0x00a0, 0x0000, 69, 163 }, /* 161: p=0.003387 ( 1, 392) */ + { 0x03d3, 0x0000, 54, 48 }, /* 162: p=0.020710 ( 110, 2) */ + { 0x0117, 0x0000, 65, 59 }, /* 163: p=0.005912 ( 2, 392) */ + { 0x05c5, 0x0000, 50, 42 }, /* 164: p=0.031250 ( 72, 2) */ + { 0x01ea, 0x0000, 167, 171 }, /* 165: p=0.010362 ( 1, 127) */ + { 0x08ad, 0x0000, 44, 38 }, /* 166: p=0.046979 ( 47, 2) */ + { 0x0144, 0x0000, 65, 169 }, /* 167: p=0.006849 ( 1, 193) */ + { 0x0ccc, 0x0000, 40, 32 }, /* 168: p=0.069306 ( 31, 2) */ + { 0x0234, 0x0000, 59, 53 }, /* 169: p=0.011925 ( 2, 193) */ + { 0x1302, 0x0000, 34, 26 }, /* 170: p=0.102940 ( 20, 2) */ + { 0x0353, 0x0000, 55, 47 }, /* 171: p=0.017995 ( 2, 127) */ + { 0x1b81, 0x0000, 30, 174 }, /* 172: p=0.148935 ( 13, 2) */ + { 0x05c5, 0x0000, 175, 193 }, /* 173: p=0.031249 ( 1, 41) */ + { 0x24ef, 0x0000, 24, 18 }, /* 174: p=0.199999 ( 13, 3) */ + { 0x03cf, 0x0000, 177, 191 }, /* 175: p=0.020618 ( 1, 63) */ + { 0x2b74, 0x0000, 178, 222 }, /* 176: p=0.235291 ( 4, 1) */ + { 0x0285, 0x0000, 179, 189 }, /* 177: p=0.013652 ( 1, 96) */ + { 0x201d, 0x0000, 180, 218 }, /* 178: p=0.173910 ( 6, 1) */ + { 0x01ab, 0x0000, 181, 187 }, /* 179: p=0.009029 ( 1, 146) */ + { 0x1715, 0x0000, 182, 216 }, /* 180: p=0.124998 ( 9, 1) */ + { 0x011a, 0x0000, 183, 185 }, /* 181: p=0.005961 ( 1, 222) */ + { 0x0fb7, 0x0000, 184, 214 }, /* 182: p=0.085105 ( 14, 1) */ + { 0x00ba, 0x0000, 69, 61 }, /* 183: p=0.003925 ( 1, 338) */ + { 0x0a67, 0x0000, 186, 212 }, /* 184: p=0.056337 ( 22, 1) */ + { 0x01eb, 0x0000, 59, 53 }, /* 185: p=0.010386 ( 2, 222) */ + { 0x06e7, 0x0000, 188, 210 }, /* 186: p=0.037382 ( 34, 1) */ + { 0x02e6, 0x0000, 55, 49 }, /* 187: p=0.015695 ( 2, 146) */ + { 0x0496, 0x0000, 190, 208 }, /* 188: p=0.024844 ( 52, 1) */ + { 0x045e, 0x0000, 51, 45 }, /* 189: p=0.023648 ( 2, 96) */ + { 0x030d, 0x0000, 192, 206 }, /* 190: p=0.016529 ( 79, 1) */ + { 0x0690, 0x0000, 47, 39 }, /* 191: p=0.035533 ( 2, 63) */ + { 0x0206, 0x0000, 194, 204 }, /* 192: p=0.010959 ( 120, 1) */ + { 0x09de, 0x0000, 41, 195 }, /* 193: p=0.053434 ( 2, 41) */ + { 0x0155, 0x0000, 196, 202 }, /* 194: p=0.007220 ( 183, 1) */ + { 0x0dc8, 0x0000, 37, 31 }, /* 195: p=0.074626 ( 3, 41) */ + { 0x00e1, 0x0000, 198, 200 }, /* 196: p=0.004750 ( 279, 1) */ + { 0x2b74, 0x0000, 199, 243 }, /* 197: p=0.235291 ( 1, 4) */ + { 0x0094, 0x0000, 72, 64 }, /* 198: p=0.003132 ( 424, 1) */ + { 0x201d, 0x0000, 201, 239 }, /* 199: p=0.173910 ( 1, 6) */ + { 0x0188, 0x0000, 62, 56 }, /* 200: p=0.008284 ( 279, 2) */ + { 0x1715, 0x0000, 203, 237 }, /* 201: p=0.124998 ( 1, 9) */ + { 0x0252, 0x0000, 58, 52 }, /* 202: p=0.012567 ( 183, 2) */ + { 0x0fb7, 0x0000, 205, 235 }, /* 203: p=0.085105 ( 1, 14) */ + { 0x0383, 0x0000, 54, 48 }, /* 204: p=0.019021 ( 120, 2) */ + { 0x0a67, 0x0000, 207, 233 }, /* 205: p=0.056337 ( 1, 22) */ + { 0x0547, 0x0000, 50, 44 }, /* 206: p=0.028571 ( 79, 2) */ + { 0x06e7, 0x0000, 209, 231 }, /* 207: p=0.037382 ( 1, 34) */ + { 0x07e2, 0x0000, 46, 38 }, /* 208: p=0.042682 ( 52, 2) */ + { 0x0496, 0x0000, 211, 229 }, /* 209: p=0.024844 ( 1, 52) */ + { 0x0bc0, 0x0000, 40, 34 }, /* 210: p=0.063636 ( 34, 2) */ + { 0x030d, 0x0000, 213, 227 }, /* 211: p=0.016529 ( 1, 79) */ + { 0x1178, 0x0000, 36, 28 }, /* 212: p=0.094593 ( 22, 2) */ + { 0x0206, 0x0000, 215, 225 }, /* 213: p=0.010959 ( 1, 120) */ + { 0x19da, 0x0000, 30, 22 }, /* 214: p=0.139999 ( 14, 2) */ + { 0x0155, 0x0000, 217, 223 }, /* 215: p=0.007220 ( 1, 183) */ + { 0x24ef, 0x0000, 26, 16 }, /* 216: p=0.199998 ( 9, 2) */ + { 0x00e1, 0x0000, 219, 221 }, /* 217: p=0.004750 ( 1, 279) */ + { 0x320e, 0x0000, 20, 220 }, /* 218: p=0.269229 ( 6, 2) */ + { 0x0094, 0x0000, 71, 63 }, /* 219: p=0.003132 ( 1, 424) */ + { 0x432a, 0x0000, 14, 8 }, /* 220: p=0.344827 ( 6, 3) */ + { 0x0188, 0x0000, 61, 55 }, /* 221: p=0.008284 ( 2, 279) */ + { 0x447d, 0x0000, 14, 224 }, /* 222: p=0.349998 ( 4, 2) */ + { 0x0252, 0x0000, 57, 51 }, /* 223: p=0.012567 ( 2, 183) */ + { 0x5ece, 0x0000, 8, 2 }, /* 224: p=0.434782 ( 4, 3) */ + { 0x0383, 0x0000, 53, 47 }, /* 225: p=0.019021 ( 2, 120) */ + { 0x8000, 0x0000, 228, 87 }, /* 226: p=0.500000 ( 1, 1) */ + { 0x0547, 0x0000, 49, 43 }, /* 227: p=0.028571 ( 2, 79) */ + { 0x481a, 0x0000, 230, 246 }, /* 228: p=0.363634 ( 2, 1) */ + { 0x07e2, 0x0000, 45, 37 }, /* 229: p=0.042682 ( 2, 52) */ + { 0x3579, 0x0000, 232, 244 }, /* 230: p=0.285711 ( 3, 1) */ + { 0x0bc0, 0x0000, 39, 33 }, /* 231: p=0.063636 ( 2, 34) */ + { 0x24ef, 0x0000, 234, 238 }, /* 232: p=0.199997 ( 5, 1) */ + { 0x1178, 0x0000, 35, 27 }, /* 233: p=0.094593 ( 2, 22) */ + { 0x1978, 0x0000, 138, 236 }, /* 234: p=0.137929 ( 8, 1) */ + { 0x19da, 0x0000, 29, 21 }, /* 235: p=0.139999 ( 2, 14) */ + { 0x2865, 0x0000, 24, 16 }, /* 236: p=0.218748 ( 8, 2) */ + { 0x24ef, 0x0000, 25, 15 }, /* 237: p=0.199998 ( 2, 9) */ + { 0x3987, 0x0000, 240, 8 }, /* 238: p=0.304346 ( 5, 2) */ + { 0x320e, 0x0000, 19, 241 }, /* 239: p=0.269229 ( 2, 6) */ + { 0x2c99, 0x0000, 22, 242 }, /* 240: p=0.241378 ( 7, 2) */ + { 0x432a, 0x0000, 13, 7 }, /* 241: p=0.344827 ( 3, 6) */ + { 0x3b5f, 0x0000, 16, 10 }, /* 242: p=0.312499 ( 7, 3) */ + { 0x447d, 0x0000, 13, 245 }, /* 243: p=0.349998 ( 2, 4) */ + { 0x5695, 0x0000, 10, 2 }, /* 244: p=0.411764 ( 3, 2) */ + { 0x5ece, 0x0000, 7, 1 }, /* 245: p=0.434782 ( 3, 4) */ + { 0x8000, 0x0000, 244, 83 }, /* 246: p=0.500000 ( 2, 2) */ + { 0x8000, 0x0000, 249, 250 }, /* 247: p=0.500000 ( 1, 1) */ + { 0x5695, 0x0000, 10, 2 }, /* 248: p=0.411764 ( 3, 2) */ + { 0x481a, 0x0000, 89, 143 }, /* 249: p=0.363634 ( 1, 2) */ + { 0x481a, 0x0000, 230, 246 }, /* 250: p=0.363634 ( 2, 1) */ +#endif +#ifdef ZCODER + /* This table has been designed for the ZCoder + * by running the following command in file 'ztable2.sn': + * (fast-crude (steady-mat 0.0035 0.0002) 260))) + */ + { 0x8000, 0x0000, 84, 139 }, /* 000: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 3, 4 }, /* 001: p=0.500000 ( 0, 0) */ + { 0x8000, 0x0000, 4, 3 }, /* 002: p=0.500000 ( 0, 0) */ + { 0x7399, 0x10a5, 5, 1 }, /* 003: p=0.465226 ( 0, 0) */ + { 0x7399, 0x10a5, 6, 2 }, /* 004: p=0.465226 ( 0, 0) */ + { 0x6813, 0x1f28, 7, 3 }, /* 005: p=0.430708 ( 0, 0) */ + { 0x6813, 0x1f28, 8, 4 }, /* 006: p=0.430708 ( 0, 0) */ + { 0x5d65, 0x2bd3, 9, 5 }, /* 007: p=0.396718 ( 0, 0) */ + { 0x5d65, 0x2bd3, 10, 6 }, /* 008: p=0.396718 ( 0, 0) */ + { 0x5387, 0x36e3, 11, 7 }, /* 009: p=0.363535 ( 0, 0) */ + { 0x5387, 0x36e3, 12, 8 }, /* 010: p=0.363535 ( 0, 0) */ + { 0x4a73, 0x408c, 13, 9 }, /* 011: p=0.331418 ( 0, 0) */ + { 0x4a73, 0x408c, 14, 10 }, /* 012: p=0.331418 ( 0, 0) */ + { 0x421f, 0x48fe, 15, 11 }, /* 013: p=0.300562 ( 0, 0) */ + { 0x421f, 0x48fe, 16, 12 }, /* 014: p=0.300562 ( 0, 0) */ + { 0x3a85, 0x5060, 17, 13 }, /* 015: p=0.271166 ( 0, 0) */ + { 0x3a85, 0x5060, 18, 14 }, /* 016: p=0.271166 ( 0, 0) */ + { 0x339b, 0x56d3, 19, 15 }, /* 017: p=0.243389 ( 0, 0) */ + { 0x339b, 0x56d3, 20, 16 }, /* 018: p=0.243389 ( 0, 0) */ + { 0x2d59, 0x5c73, 21, 17 }, /* 019: p=0.217351 ( 0, 0) */ + { 0x2d59, 0x5c73, 22, 18 }, /* 020: p=0.217351 ( 0, 0) */ + { 0x27b3, 0x615e, 23, 19 }, /* 021: p=0.193091 ( 0, 0) */ + { 0x27b3, 0x615e, 24, 20 }, /* 022: p=0.193091 ( 0, 0) */ + { 0x22a1, 0x65a7, 25, 21 }, /* 023: p=0.170683 ( 0, 0) */ + { 0x22a1, 0x65a7, 26, 22 }, /* 024: p=0.170683 ( 0, 0) */ + { 0x1e19, 0x6963, 27, 23 }, /* 025: p=0.150134 ( 0, 0) */ + { 0x1e19, 0x6963, 28, 24 }, /* 026: p=0.150134 ( 0, 0) */ + { 0x1a0f, 0x6ca3, 29, 25 }, /* 027: p=0.131397 ( 0, 0) */ + { 0x1a0f, 0x6ca3, 30, 26 }, /* 028: p=0.131397 ( 0, 0) */ + { 0x167b, 0x6f75, 31, 27 }, /* 029: p=0.114441 ( 0, 0) */ + { 0x167b, 0x6f75, 32, 28 }, /* 030: p=0.114441 ( 0, 0) */ + { 0x1353, 0x71e6, 33, 29 }, /* 031: p=0.099214 ( 0, 0) */ + { 0x1353, 0x71e6, 34, 30 }, /* 032: p=0.099214 ( 0, 0) */ + { 0x108d, 0x7403, 35, 31 }, /* 033: p=0.085616 ( 0, 0) */ + { 0x108d, 0x7403, 36, 32 }, /* 034: p=0.085616 ( 0, 0) */ + { 0x0e1f, 0x75d7, 37, 33 }, /* 035: p=0.073525 ( 0, 0) */ + { 0x0e1f, 0x75d7, 38, 34 }, /* 036: p=0.073525 ( 0, 0) */ + { 0x0c01, 0x7769, 39, 35 }, /* 037: p=0.062871 ( 0, 0) */ + { 0x0c01, 0x7769, 40, 36 }, /* 038: p=0.062871 ( 0, 0) */ + { 0x0a2b, 0x78c2, 41, 37 }, /* 039: p=0.053524 ( 0, 0) */ + { 0x0a2b, 0x78c2, 42, 38 }, /* 040: p=0.053524 ( 0, 0) */ + { 0x0895, 0x79ea, 43, 39 }, /* 041: p=0.045374 ( 0, 0) */ + { 0x0895, 0x79ea, 44, 40 }, /* 042: p=0.045374 ( 0, 0) */ + { 0x0737, 0x7ae7, 45, 41 }, /* 043: p=0.038280 ( 0, 0) */ + { 0x0737, 0x7ae7, 46, 42 }, /* 044: p=0.038280 ( 0, 0) */ + { 0x060b, 0x7bbe, 47, 43 }, /* 045: p=0.032175 ( 0, 0) */ + { 0x060b, 0x7bbe, 48, 44 }, /* 046: p=0.032175 ( 0, 0) */ + { 0x050b, 0x7c75, 49, 45 }, /* 047: p=0.026926 ( 0, 0) */ + { 0x050b, 0x7c75, 50, 46 }, /* 048: p=0.026926 ( 0, 0) */ + { 0x0431, 0x7d10, 51, 47 }, /* 049: p=0.022430 ( 0, 0) */ + { 0x0431, 0x7d10, 52, 48 }, /* 050: p=0.022430 ( 0, 0) */ + { 0x0379, 0x7d92, 53, 49 }, /* 051: p=0.018623 ( 0, 0) */ + { 0x0379, 0x7d92, 54, 50 }, /* 052: p=0.018623 ( 0, 0) */ + { 0x02dd, 0x7dff, 55, 51 }, /* 053: p=0.015386 ( 0, 0) */ + { 0x02dd, 0x7dff, 56, 52 }, /* 054: p=0.015386 ( 0, 0) */ + { 0x025b, 0x7e5b, 57, 53 }, /* 055: p=0.012671 ( 0, 0) */ + { 0x025b, 0x7e5b, 58, 54 }, /* 056: p=0.012671 ( 0, 0) */ + { 0x01ef, 0x7ea7, 59, 55 }, /* 057: p=0.010414 ( 0, 0) */ + { 0x01ef, 0x7ea7, 60, 56 }, /* 058: p=0.010414 ( 0, 0) */ + { 0x0195, 0x7ee6, 61, 57 }, /* 059: p=0.008529 ( 0, 0) */ + { 0x0195, 0x7ee6, 62, 58 }, /* 060: p=0.008529 ( 0, 0) */ + { 0x0149, 0x7f1b, 63, 59 }, /* 061: p=0.006935 ( 0, 0) */ + { 0x0149, 0x7f1b, 64, 60 }, /* 062: p=0.006935 ( 0, 0) */ + { 0x010b, 0x7f46, 65, 61 }, /* 063: p=0.005631 ( 0, 0) */ + { 0x010b, 0x7f46, 66, 62 }, /* 064: p=0.005631 ( 0, 0) */ + { 0x00d5, 0x7f6c, 67, 63 }, /* 065: p=0.004495 ( 0, 0) */ + { 0x00d5, 0x7f6c, 68, 64 }, /* 066: p=0.004495 ( 0, 0) */ + { 0x00a5, 0x7f8d, 69, 65 }, /* 067: p=0.003484 ( 0, 0) */ + { 0x00a5, 0x7f8d, 70, 66 }, /* 068: p=0.003484 ( 0, 0) */ + { 0x007b, 0x7faa, 71, 67 }, /* 069: p=0.002592 ( 0, 0) */ + { 0x007b, 0x7faa, 72, 68 }, /* 070: p=0.002592 ( 0, 0) */ + { 0x0057, 0x7fc3, 73, 69 }, /* 071: p=0.001835 ( 0, 0) */ + { 0x0057, 0x7fc3, 74, 70 }, /* 072: p=0.001835 ( 0, 0) */ + { 0x0039, 0x7fd8, 75, 71 }, /* 073: p=0.001211 ( 0, 0) */ + { 0x0039, 0x7fd8, 76, 72 }, /* 074: p=0.001211 ( 0, 0) */ + { 0x0023, 0x7fe7, 77, 73 }, /* 075: p=0.000740 ( 0, 0) */ + { 0x0023, 0x7fe7, 78, 74 }, /* 076: p=0.000740 ( 0, 0) */ + { 0x0013, 0x7ff2, 79, 75 }, /* 077: p=0.000402 ( 0, 0) */ + { 0x0013, 0x7ff2, 80, 76 }, /* 078: p=0.000402 ( 0, 0) */ + { 0x0007, 0x7ffa, 81, 77 }, /* 079: p=0.000153 ( 0, 0) */ + { 0x0007, 0x7ffa, 82, 78 }, /* 080: p=0.000153 ( 0, 0) */ + { 0x0001, 0x7fff, 81, 79 }, /* 081: p=0.000027 ( 0, 0) */ + { 0x0001, 0x7fff, 82, 80 }, /* 082: p=0.000027 ( 0, 0) */ + { 0x620b, 0x0000, 9, 85 }, /* 083: p=0.411764 ( 2, 3) */ + { 0x294a, 0x0000, 86, 216 }, /* 084: p=0.199988 ( 1, 0) */ + { 0x8000, 0x0000, 5, 6 }, /* 085: p=0.500000 ( 3, 3) */ + { 0x0db3, 0x0000, 88, 168 }, /* 086: p=0.071422 ( 4, 0) */ + { 0x538e, 0x0000, 89, 137 }, /* 087: p=0.363634 ( 1, 2) */ + { 0x0490, 0x0000, 90, 134 }, /* 088: p=0.024388 ( 13, 0) */ + { 0x3e3e, 0x0000, 91, 135 }, /* 089: p=0.285711 ( 1, 3) */ + { 0x017c, 0x0000, 92, 112 }, /* 090: p=0.007999 ( 41, 0) */ + { 0x294a, 0x0000, 93, 133 }, /* 091: p=0.199997 ( 1, 5) */ + { 0x007c, 0x0000, 94, 104 }, /* 092: p=0.002611 ( 127, 0) */ + { 0x1b75, 0x0000, 95, 131 }, /* 093: p=0.137929 ( 1, 8) */ + { 0x0028, 0x0000, 96, 100 }, /* 094: p=0.000849 ( 392, 0) */ + { 0x12fc, 0x0000, 97, 129 }, /* 095: p=0.097559 ( 1, 12) */ + { 0x000d, 0x0000, 82, 98 }, /* 096: p=0.000276 ( 1208, 0) */ + { 0x0cfb, 0x0000, 99, 125 }, /* 097: p=0.067795 ( 1, 18) */ + { 0x0034, 0x0000, 76, 72 }, /* 098: p=0.001102 ( 1208, 1) */ + { 0x08cd, 0x0000, 101, 123 }, /* 099: p=0.046511 ( 1, 27) */ + { 0x00a0, 0x0000, 70, 102 }, /* 100: p=0.003387 ( 392, 1) */ + { 0x05de, 0x0000, 103, 119 }, /* 101: p=0.031249 ( 1, 41) */ + { 0x0118, 0x0000, 66, 60 }, /* 102: p=0.005912 ( 392, 2) */ + { 0x03e9, 0x0000, 105, 117 }, /* 103: p=0.020942 ( 1, 62) */ + { 0x01ed, 0x0000, 106, 110 }, /* 104: p=0.010362 ( 127, 1) */ + { 0x0298, 0x0000, 107, 115 }, /* 105: p=0.013937 ( 1, 94) */ + { 0x0145, 0x0000, 66, 108 }, /* 106: p=0.006849 ( 193, 1) */ + { 0x01b6, 0x0000, 109, 113 }, /* 107: p=0.009216 ( 1, 143) */ + { 0x0237, 0x0000, 60, 54 }, /* 108: p=0.011925 ( 193, 2) */ + { 0x0121, 0x0000, 65, 111 }, /* 109: p=0.006097 ( 1, 217) */ + { 0x035b, 0x0000, 56, 48 }, /* 110: p=0.017995 ( 127, 2) */ + { 0x01f9, 0x0000, 59, 53 }, /* 111: p=0.010622 ( 2, 217) */ + { 0x05de, 0x0000, 114, 130 }, /* 112: p=0.031249 ( 41, 1) */ + { 0x02fc, 0x0000, 55, 49 }, /* 113: p=0.016018 ( 2, 143) */ + { 0x03e9, 0x0000, 116, 128 }, /* 114: p=0.020942 ( 62, 1) */ + { 0x0484, 0x0000, 51, 45 }, /* 115: p=0.024138 ( 2, 94) */ + { 0x0298, 0x0000, 118, 126 }, /* 116: p=0.013937 ( 94, 1) */ + { 0x06ca, 0x0000, 47, 39 }, /* 117: p=0.036082 ( 2, 62) */ + { 0x01b6, 0x0000, 120, 124 }, /* 118: p=0.009216 ( 143, 1) */ + { 0x0a27, 0x0000, 41, 121 }, /* 119: p=0.053434 ( 2, 41) */ + { 0x0121, 0x0000, 66, 122 }, /* 120: p=0.006097 ( 217, 1) */ + { 0x0e57, 0x0000, 37, 31 }, /* 121: p=0.074626 ( 3, 41) */ + { 0x01f9, 0x0000, 60, 54 }, /* 122: p=0.010622 ( 217, 2) */ + { 0x0f25, 0x0000, 37, 29 }, /* 123: p=0.078651 ( 2, 27) */ + { 0x02fc, 0x0000, 56, 50 }, /* 124: p=0.016018 ( 143, 2) */ + { 0x1629, 0x0000, 33, 127 }, /* 125: p=0.112902 ( 2, 18) */ + { 0x0484, 0x0000, 52, 46 }, /* 126: p=0.024138 ( 94, 2) */ + { 0x1ee8, 0x0000, 27, 21 }, /* 127: p=0.153845 ( 3, 18) */ + { 0x06ca, 0x0000, 48, 40 }, /* 128: p=0.036082 ( 62, 2) */ + { 0x200f, 0x0000, 27, 19 }, /* 129: p=0.159089 ( 2, 12) */ + { 0x0a27, 0x0000, 42, 132 }, /* 130: p=0.053434 ( 41, 2) */ + { 0x2dae, 0x0000, 21, 15 }, /* 131: p=0.218748 ( 2, 8) */ + { 0x0e57, 0x0000, 38, 32 }, /* 132: p=0.074626 ( 41, 3) */ + { 0x4320, 0x0000, 15, 7 }, /* 133: p=0.304346 ( 2, 5) */ + { 0x11a0, 0x0000, 136, 164 }, /* 134: p=0.090907 ( 13, 1) */ + { 0x620b, 0x0000, 9, 85 }, /* 135: p=0.411764 ( 2, 3) */ + { 0x0bbe, 0x0000, 138, 162 }, /* 136: p=0.061537 ( 20, 1) */ + { 0x8000, 0x0000, 135, 248 }, /* 137: p=0.500000 ( 2, 2) */ + { 0x07f3, 0x0000, 140, 160 }, /* 138: p=0.042104 ( 30, 1) */ + { 0x294a, 0x0000, 141, 247 }, /* 139: p=0.199988 ( 0, 1) */ + { 0x053e, 0x0000, 142, 158 }, /* 140: p=0.027971 ( 46, 1) */ + { 0x0db3, 0x0000, 143, 199 }, /* 141: p=0.071422 ( 0, 4) */ + { 0x0378, 0x0000, 144, 156 }, /* 142: p=0.018604 ( 70, 1) */ + { 0x0490, 0x0000, 145, 167 }, /* 143: p=0.024388 ( 0, 13) */ + { 0x024d, 0x0000, 146, 154 }, /* 144: p=0.012384 ( 106, 1) */ + { 0x017c, 0x0000, 147, 101 }, /* 145: p=0.007999 ( 0, 41) */ + { 0x0185, 0x0000, 148, 152 }, /* 146: p=0.008197 ( 161, 1) */ + { 0x007c, 0x0000, 149, 159 }, /* 147: p=0.002611 ( 0, 127) */ + { 0x0100, 0x0000, 68, 150 }, /* 148: p=0.005405 ( 245, 1) */ + { 0x0028, 0x0000, 151, 155 }, /* 149: p=0.000849 ( 0, 392) */ + { 0x01c0, 0x0000, 62, 56 }, /* 150: p=0.009421 ( 245, 2) */ + { 0x000d, 0x0000, 81, 153 }, /* 151: p=0.000276 ( 0, 1208) */ + { 0x02a7, 0x0000, 58, 52 }, /* 152: p=0.014256 ( 161, 2) */ + { 0x0034, 0x0000, 75, 71 }, /* 153: p=0.001102 ( 1, 1208) */ + { 0x0403, 0x0000, 54, 46 }, /* 154: p=0.021472 ( 106, 2) */ + { 0x00a0, 0x0000, 69, 157 }, /* 155: p=0.003387 ( 1, 392) */ + { 0x0608, 0x0000, 48, 42 }, /* 156: p=0.032110 ( 70, 2) */ + { 0x0118, 0x0000, 65, 59 }, /* 157: p=0.005912 ( 2, 392) */ + { 0x0915, 0x0000, 44, 38 }, /* 158: p=0.047945 ( 46, 2) */ + { 0x01ed, 0x0000, 161, 165 }, /* 159: p=0.010362 ( 1, 127) */ + { 0x0db4, 0x0000, 40, 32 }, /* 160: p=0.071428 ( 30, 2) */ + { 0x0145, 0x0000, 65, 163 }, /* 161: p=0.006849 ( 1, 193) */ + { 0x1417, 0x0000, 34, 26 }, /* 162: p=0.102940 ( 20, 2) */ + { 0x0237, 0x0000, 59, 53 }, /* 163: p=0.011925 ( 2, 193) */ + { 0x1dd6, 0x0000, 30, 166 }, /* 164: p=0.148935 ( 13, 2) */ + { 0x035b, 0x0000, 55, 47 }, /* 165: p=0.017995 ( 2, 127) */ + { 0x294a, 0x0000, 24, 18 }, /* 166: p=0.199999 ( 13, 3) */ + { 0x11a0, 0x0000, 169, 195 }, /* 167: p=0.090907 ( 1, 13) */ + { 0x31a3, 0x0000, 170, 212 }, /* 168: p=0.235291 ( 4, 1) */ + { 0x0bbe, 0x0000, 171, 193 }, /* 169: p=0.061537 ( 1, 20) */ + { 0x235a, 0x0000, 172, 208 }, /* 170: p=0.173910 ( 6, 1) */ + { 0x07f3, 0x0000, 173, 191 }, /* 171: p=0.042104 ( 1, 30) */ + { 0x18b3, 0x0000, 174, 206 }, /* 172: p=0.124998 ( 9, 1) */ + { 0x053e, 0x0000, 175, 189 }, /* 173: p=0.027971 ( 1, 46) */ + { 0x1073, 0x0000, 176, 204 }, /* 174: p=0.085105 ( 14, 1) */ + { 0x0378, 0x0000, 177, 187 }, /* 175: p=0.018604 ( 1, 70) */ + { 0x0b35, 0x0000, 178, 200 }, /* 176: p=0.058822 ( 21, 1) */ + { 0x024d, 0x0000, 179, 185 }, /* 177: p=0.012384 ( 1, 106) */ + { 0x0778, 0x0000, 180, 198 }, /* 178: p=0.039603 ( 32, 1) */ + { 0x0185, 0x0000, 181, 183 }, /* 179: p=0.008197 ( 1, 161) */ + { 0x04ed, 0x0000, 182, 194 }, /* 180: p=0.026315 ( 49, 1) */ + { 0x0100, 0x0000, 67, 59 }, /* 181: p=0.005405 ( 1, 245) */ + { 0x0349, 0x0000, 184, 192 }, /* 182: p=0.017621 ( 74, 1) */ + { 0x02a7, 0x0000, 57, 51 }, /* 183: p=0.014256 ( 2, 161) */ + { 0x022e, 0x0000, 186, 190 }, /* 184: p=0.011730 ( 112, 1) */ + { 0x0403, 0x0000, 53, 45 }, /* 185: p=0.021472 ( 2, 106) */ + { 0x0171, 0x0000, 64, 188 }, /* 186: p=0.007767 ( 170, 1) */ + { 0x0608, 0x0000, 47, 41 }, /* 187: p=0.032110 ( 2, 70) */ + { 0x0283, 0x0000, 58, 52 }, /* 188: p=0.013513 ( 170, 2) */ + { 0x0915, 0x0000, 43, 37 }, /* 189: p=0.047945 ( 2, 46) */ + { 0x03cc, 0x0000, 54, 48 }, /* 190: p=0.020349 ( 112, 2) */ + { 0x0db4, 0x0000, 39, 31 }, /* 191: p=0.071428 ( 2, 30) */ + { 0x05b6, 0x0000, 50, 42 }, /* 192: p=0.030434 ( 74, 2) */ + { 0x1417, 0x0000, 33, 25 }, /* 193: p=0.102940 ( 2, 20) */ + { 0x088a, 0x0000, 44, 196 }, /* 194: p=0.045161 ( 49, 2) */ + { 0x1dd6, 0x0000, 29, 197 }, /* 195: p=0.148935 ( 2, 13) */ + { 0x0c16, 0x0000, 40, 34 }, /* 196: p=0.063291 ( 49, 3) */ + { 0x294a, 0x0000, 23, 17 }, /* 197: p=0.199999 ( 3, 13) */ + { 0x0ce2, 0x0000, 40, 32 }, /* 198: p=0.067307 ( 32, 2) */ + { 0x31a3, 0x0000, 201, 243 }, /* 199: p=0.235291 ( 1, 4) */ + { 0x1332, 0x0000, 36, 202 }, /* 200: p=0.098590 ( 21, 2) */ + { 0x235a, 0x0000, 203, 239 }, /* 201: p=0.173910 ( 1, 6) */ + { 0x1adc, 0x0000, 30, 24 }, /* 202: p=0.135134 ( 21, 3) */ + { 0x18b3, 0x0000, 205, 237 }, /* 203: p=0.124998 ( 1, 9) */ + { 0x1be7, 0x0000, 30, 22 }, /* 204: p=0.139999 ( 14, 2) */ + { 0x1073, 0x0000, 207, 235 }, /* 205: p=0.085105 ( 1, 14) */ + { 0x294a, 0x0000, 26, 16 }, /* 206: p=0.199998 ( 9, 2) */ + { 0x0b35, 0x0000, 209, 231 }, /* 207: p=0.058822 ( 1, 21) */ + { 0x3a07, 0x0000, 20, 210 }, /* 208: p=0.269229 ( 6, 2) */ + { 0x0778, 0x0000, 211, 229 }, /* 209: p=0.039603 ( 1, 32) */ + { 0x4e30, 0x0000, 14, 8 }, /* 210: p=0.344827 ( 6, 3) */ + { 0x04ed, 0x0000, 213, 225 }, /* 211: p=0.026315 ( 1, 49) */ + { 0x4fa6, 0x0000, 14, 214 }, /* 212: p=0.349998 ( 4, 2) */ + { 0x0349, 0x0000, 215, 223 }, /* 213: p=0.017621 ( 1, 74) */ + { 0x6966, 0x0000, 8, 2 }, /* 214: p=0.434782 ( 4, 3) */ + { 0x022e, 0x0000, 217, 221 }, /* 215: p=0.011730 ( 1, 112) */ + { 0x8000, 0x0000, 218, 87 }, /* 216: p=0.500000 ( 1, 1) */ + { 0x0171, 0x0000, 63, 219 }, /* 217: p=0.007767 ( 1, 170) */ + { 0x538e, 0x0000, 220, 246 }, /* 218: p=0.363634 ( 2, 1) */ + { 0x0283, 0x0000, 57, 51 }, /* 219: p=0.013513 ( 2, 170) */ + { 0x3e3e, 0x0000, 222, 244 }, /* 220: p=0.285711 ( 3, 1) */ + { 0x03cc, 0x0000, 53, 47 }, /* 221: p=0.020349 ( 2, 112) */ + { 0x294a, 0x0000, 224, 242 }, /* 222: p=0.199997 ( 5, 1) */ + { 0x05b6, 0x0000, 49, 41 }, /* 223: p=0.030434 ( 2, 74) */ + { 0x1b75, 0x0000, 226, 240 }, /* 224: p=0.137929 ( 8, 1) */ + { 0x088a, 0x0000, 43, 227 }, /* 225: p=0.045161 ( 2, 49) */ + { 0x12fc, 0x0000, 228, 238 }, /* 226: p=0.097559 ( 12, 1) */ + { 0x0c16, 0x0000, 39, 33 }, /* 227: p=0.063291 ( 3, 49) */ + { 0x0cfb, 0x0000, 230, 234 }, /* 228: p=0.067795 ( 18, 1) */ + { 0x0ce2, 0x0000, 39, 31 }, /* 229: p=0.067307 ( 2, 32) */ + { 0x08cd, 0x0000, 112, 232 }, /* 230: p=0.046511 ( 27, 1) */ + { 0x1332, 0x0000, 35, 233 }, /* 231: p=0.098590 ( 2, 21) */ + { 0x0f25, 0x0000, 38, 30 }, /* 232: p=0.078651 ( 27, 2) */ + { 0x1adc, 0x0000, 29, 23 }, /* 233: p=0.135134 ( 3, 21) */ + { 0x1629, 0x0000, 34, 236 }, /* 234: p=0.112902 ( 18, 2) */ + { 0x1be7, 0x0000, 29, 21 }, /* 235: p=0.139999 ( 2, 14) */ + { 0x1ee8, 0x0000, 28, 22 }, /* 236: p=0.153845 ( 18, 3) */ + { 0x294a, 0x0000, 25, 15 }, /* 237: p=0.199998 ( 2, 9) */ + { 0x200f, 0x0000, 28, 20 }, /* 238: p=0.159089 ( 12, 2) */ + { 0x3a07, 0x0000, 19, 241 }, /* 239: p=0.269229 ( 2, 6) */ + { 0x2dae, 0x0000, 22, 16 }, /* 240: p=0.218748 ( 8, 2) */ + { 0x4e30, 0x0000, 13, 7 }, /* 241: p=0.344827 ( 3, 6) */ + { 0x4320, 0x0000, 16, 8 }, /* 242: p=0.304346 ( 5, 2) */ + { 0x4fa6, 0x0000, 13, 245 }, /* 243: p=0.349998 ( 2, 4) */ + { 0x620b, 0x0000, 10, 2 }, /* 244: p=0.411764 ( 3, 2) */ + { 0x6966, 0x0000, 7, 1 }, /* 245: p=0.434782 ( 3, 4) */ + { 0x8000, 0x0000, 244, 83 }, /* 246: p=0.500000 ( 2, 2) */ + { 0x8000, 0x0000, 249, 250 }, /* 247: p=0.500000 ( 1, 1) */ + { 0x620b, 0x0000, 10, 2 }, /* 248: p=0.411764 ( 3, 2) */ + { 0x538e, 0x0000, 89, 137 }, /* 249: p=0.363634 ( 1, 2) */ + { 0x538e, 0x0000, 220, 246 }, /* 250: p=0.363634 ( 2, 1) */ +#endif +}; + + + +//////////////////////////////////////////////////////////////// +// CONSTRUCTOR/DESTRUCTOR +//////////////////////////////////////////////////////////////// + +class ZPCodec::Encode : public ZPCodec +{ +public: + Encode(GP<ByteStream> gbs, const bool djvucompat); + virtual ~Encode(); +private: + void init(void); +}; + +ZPCodec::Encode::Encode(GP<ByteStream> gbs, const bool djvucompat) +: ZPCodec(gbs,true,djvucompat) +{ + init(); + // Codebit counter +#ifdef ZPCODEC_BITCOUNT + bitcount = 0; +#endif +} + +ZPCodec::Encode::~Encode() +{ + eflush(); +} + +class ZPCodec::Decode : public ZPCodec +{ +public: + Decode(GP<ByteStream> gbs, const bool djvucompat); + virtual ~Decode(); +private: + void init(void); +}; + +ZPCodec::Decode::Decode(GP<ByteStream> gbs, const bool djvucompat) +: ZPCodec(gbs,false,djvucompat) +{ + init(); + // Codebit counter +#ifdef ZPCODEC_BITCOUNT + bitcount = 0; +#endif +} + +ZPCodec::Decode::~Decode() {} + +ZPCodec::ZPCodec(GP<ByteStream> xgbs, const bool xencoding, const bool djvucompat) +: gbs(xgbs), bs(xgbs), encoding(xencoding), fence(0), subend(0), buffer(0), nrun(0) +{ + // Create machine independent ffz table + for (int i=0; i<256; i++) + { + ffzt[i]=0; + for (int j=i; j&0x80; j<<=1) + ffzt[i] += 1; + } + // Initialize table + newtable(default_ztable); + // Patch table table (and lose DjVu compatibility). + if (!djvucompat) + { + for (int j=0; j<256; j++) + { + unsigned short a = 0x10000-p[j]; + while (a>=0x8000) a=(unsigned short)(a<<1); + if (m[j]>0 && a+p[j]>=0x8000 && a>=m[j]) + { + BitContext x = default_ztable[j].dn; + BitContext y = default_ztable[x].dn; + dn[j] = y; + } + } + } +} + +ZPCodec::~ZPCodec() {} + +GP<ZPCodec> +ZPCodec::create(GP<ByteStream> gbs, const bool encoding, const bool djvucompat) +{ + GP<ZPCodec> retval; + if(encoding) + { + retval=new ZPCodec::Encode(gbs,djvucompat); + }else + { + retval=new ZPCodec::Decode(gbs,djvucompat); + } + return retval; +} + +//////////////////////////////////////////////////////////////// +// Z CODER DECODE ALGORITHM +//////////////////////////////////////////////////////////////// + + + +void +ZPCodec::Decode::init(void) +{ + assert(sizeof(unsigned int)==4); + assert(sizeof(unsigned short)==2); + a = 0; + /* Read first 16 bits of code */ + if (! bs->read((void*)&byte, 1)) + byte = 0xff; + code = (byte<<8); + if (! bs->read((void*)&byte, 1)) + byte = 0xff; + code = code | byte; + /* Preload buffer */ + delay = 25; + scount = 0; + preload(); + /* Compute initial fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; +} + + +void +ZPCodec::preload(void) +{ + while (scount<=24) + { + if (bs->read((void*)&byte, 1) < 1) + { + byte = 0xff; + if (--delay < 1) + G_THROW( ByteStream::EndOfFile ); + } + buffer = (buffer<<8) | byte; + scount += 8; + } +} + + +inline int +ZPCodec::ffz(unsigned int x) +{ + // DO NOT DEFINE FASTBSR : + // Test shows that it hardly helps on a PPro, + // and rumors are that BSR is very slow on PPlain. +#if defined(FASTBSR) && defined(_MSC_VER) && defined(_M_IX86) + int r; + __asm { + mov ebx, x + xor ebx, 0xffff + mov eax, -1 + bsr eax, ebx + mov r, eax + } + return 15 - r; +#elif defined(FASTBSR) && defined(__GNUC__) && defined(__i386__) + int r, dummy; + __asm__ const ( "movl %2,%1\n\t" + "xorl $0xffff, %1\n\t" + "movl $-1, %0\n\t" + "bsrl %1, %0" + : "=&q" (r), "=q" (dummy) : "rm" (x) ); + return 15 - r; +#else + return (x>=0xff00) ? (ffzt[x&0xff]+8) : (ffzt[(x>>8)&0xff]); +#endif +} + + +int +ZPCodec::decode_sub(BitContext &ctx, unsigned int z) +{ + /* Save bit */ + int bit = (ctx & 1); + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS adaptation */ + ctx = dn[ctx]; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return bit ^ 1; + } + else + { + /* MPS adaptation */ + if (a >= m[ctx]) + ctx = up[ctx]; + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return bit; + } +} + + +int +ZPCodec::decode_sub_simple(int mps, unsigned int z) +{ + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps ^ 1; + } + else + { + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps; + } +} + + +int +ZPCodec::decode_sub_nolearn(int mps, unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Test MPS/LPS */ + if (z > code) + { + /* LPS branch */ + z = 0x10000 - z; + a = a + z; + code = code + z; + /* LPS renormalization */ + int shift = ffz(a); + scount -= shift; + a = (unsigned short)(a<<shift); + code = (unsigned short)(code<<shift) | ((buffer>>scount) & ((1<<shift)-1)); +#ifdef ZPCODEC_BITCOUNT + bitcount += shift; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps ^ 1; + } + else + { + /* MPS renormalization */ + scount -= 1; + a = (unsigned short)(z<<1); + code = (unsigned short)(code<<1) | ((buffer>>scount) & 1); +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif + if (scount<16) preload(); + /* Adjust fence */ + fence = code; + if (code >= 0x8000) + fence = 0x7fff; + return mps; + } +} + + + + + +//////////////////////////////////////////////////////////////// +// Z CODER ENCODE ALGORITHM +//////////////////////////////////////////////////////////////// + + + + +void +ZPCodec::Encode::init(void) +{ + assert(sizeof(unsigned int)==4); + assert(sizeof(unsigned short)==2); + a = 0; + scount = 0; + byte = 0; + delay = 25; + subend = 0; + buffer = 0xffffff; + nrun = 0; +} + +void +ZPCodec::outbit(int bit) +{ + if (delay > 0) + { + if (delay < 0xff) // delay=0xff suspends emission forever + delay -= 1; + } + else + { + /* Insert a bit */ + byte = (byte<<1) | bit; + /* Output a byte */ + if (++scount == 8) + { + if (!encoding) + G_THROW( ERR_MSG("ZPCodec.no_encoding") ); + if (bs->write((void*)&byte, 1) != 1) + G_THROW( ERR_MSG("ZPCodec.write_error") ); + scount = 0; + byte = 0; + } + } +} + +void +ZPCodec::zemit(int b) +{ + /* Shift new bit into 3bytes buffer */ + buffer = (buffer<<1) + b; + /* Examine bit going out of the 3bytes buffer */ + b = (buffer >> 24); + buffer = (buffer & 0xffffff); + /* The following lines have been changed in order to emphazise the + * similarity between this bit counting and the scheme of Witten, Neal & Cleary + * (WN&C). Corresponding changes have been made in outbit and eflush. + * Variable 'nrun' is similar to the 'bits_to_follow' in the W&N code. + */ + switch(b) + { + /* Similar to WN&C upper renormalization */ + case 1: + outbit(1); + while (nrun-- > 0) + outbit(0); + nrun = 0; + break; + /* Similar to WN&C lower renormalization */ + case 0xff: + outbit(0); + while (nrun-- > 0) + outbit(1); + nrun = 0; + break; + /* Similar to WN&C central renormalization */ + case 0: + nrun += 1; + break; + default: + assert(0); + } + /* Code bit counter */ +#ifdef ZPCODEC_BITCOUNT + bitcount += 1; +#endif +} + +void +ZPCodec::eflush() +{ + /* adjust subend */ + if (subend > 0x8000) + subend = 0x10000; + else if (subend > 0) + subend = 0x8000; + /* zemit many mps bits */ + while (buffer != 0xffffff || subend ) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + } + /* zemit pending run */ + outbit(1); + while (nrun-- > 0) + outbit(0); + nrun = 0; + /* zemit 1 until full byte */ + while (scount > 0) + outbit(1); + /* prevent further emission */ + delay = 0xff; +} + +void +ZPCodec::encode_mps(BitContext &ctx, unsigned int z) +{ + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Adaptation */ + if (a >= m[ctx]) + ctx = up[ctx]; + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_lps(BitContext &ctx, unsigned int z) +{ + /* Avoid interval reversion */ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Adaptation */ + ctx = dn[ctx]; + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_mps_simple(unsigned int z) +{ + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + +void +ZPCodec::encode_lps_simple(unsigned int z) +{ + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_mps_nolearn(unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Code MPS */ + a = z; + /* Export bits */ + if (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + +void +ZPCodec::encode_lps_nolearn(unsigned int z) +{ +#ifdef ZPCODER + unsigned int d = 0x6000 + ((z+a)>>2); + if (z > d) + z = d; +#endif +#ifdef ZCODER + if (z >= 0x8000) + z = 0x4000 + (z>>1); +#endif + /* Code LPS */ + z = 0x10000 - z; + subend += z; + a += z; + /* Export bits */ + while (a >= 0x8000) + { + zemit(1 - (subend>>15) ); + subend = (unsigned short)(subend<<1); + a = (unsigned short)(a<<1); + } +} + + + + + + +//////////////////////////////////////////////////////////////// +// TABLE AND PARAMETER MANAGEMENT +//////////////////////////////////////////////////////////////// + + + + +void +ZPCodec::newtable(ZPCodec::Table *table) +{ + for (int i=0; i<256; i++) + { + p[i] = table[i].p; + m[i] = table[i].m; + up[i] = table[i].up; + dn[i] = table[i].dn; + } +} + +static float +p_to_plps(unsigned short p) +{ + float fplps; + float fp = (float)(p) / (float)(0x10000); + const float log2 = (float)0.69314718055994530942; +#ifdef ZCODER + fplps = fp - (fp+0.5) * log(fp+0.5) + (fp-0.5)*log2; +#endif +#ifdef ZPCODER + if (fp <= (1.0/6.0) ) + fplps = fp * 2 * log2; + else + fplps = (float)((1.5*fp-0.25) - (1.5*fp+0.25)*log(1.5*fp+0.25) + (0.5*fp-0.25)*log2); +#endif + return fplps; +} + + +BitContext +ZPCodec::state(float prob1) +{ + // Return a state representing 'prob1' in the steady chain + // FixMe: This is quite slow! + int mps = (prob1 <= 0.5 ? 0 : 1); + float plps = (float)(mps ? 1.0 - prob1 : prob1); + // Locate steady chain (ordered, decreasing) + int sz = 0; + int lo = (mps ? 1 : 2); + while (p[lo+sz+sz+2] < p[lo+sz+sz]) sz+=1; + // Bisection + while (sz > 1) + { + int nsz = sz >> 1; + float nplps = p_to_plps( p[lo+nsz+nsz] ); + if (nplps < plps) + { sz=nsz; } + else + { lo=lo+nsz+nsz; sz=sz-nsz; } + } + // Choose closest one + float f1 = p_to_plps(p[lo])-plps; + float f2 = plps-p_to_plps(p[lo+2]); + return (f1<f2) ? lo : lo+2; +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/ZPCodec.h b/kviewshell/plugins/djvu/libdjvu/ZPCodec.h new file mode 100644 index 00000000..4eba6901 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/ZPCodec.h @@ -0,0 +1,747 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: ZPCodec.h,v 1.9 2003/11/07 22:08:22 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _ZPCODEC_H +#define _ZPCODEC_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +// From: Leon Bottou, 1/31/2002 +// Almost equal to my initial code. + +#include "GContainer.h" + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + +class ByteStream; + + + +/** @name ZPCodec.h + + Files #"ZPCodec.h"# and #"ZPCodec.cpp"# implement a fast binary adaptive + quasi-arithmetic coder named ZP-Coder. Because of its speed and + convenience, the ZP-Coder is used in several parts of the DjVu reference + library (See \Ref{BSByteStream.h}, \Ref{JB2Image.h}, \Ref{IW44Image.h}). + The following comments avoid the theory (see the historical remarks for + useful pointers) and concentrate on the user perspective on the ZP-Coder. + + {\bf Introduction} --- + Encoding consists of transforming a sequence of {\em message bits} into a + sequence of {\em code bits}. Decoding consists of retrieving the message + bits using only the code bits. We can make the code smaller than the + message as soon as we can predict a message bit on the basis of a {\em + coding context} composed of previously encoded or decoded bits. If the + prediction is always correct, we do not even need to encode the message + bit. If the prediction is totally unreliable, we need to generate one code + bit in order to unambiguously specify the message bit. In other words, + the more reliable the prediction, the more compression we get. + + The ZP-Coder handles prediction by means of {\em context variables} (see + \Ref{BitContext}). There must be a context variable for each possible + combination of context bits. Both the encoder and the decoder use same + context variable for coding each message bit. For instance, we can code a + binary image by successively coding all the pixels (the message bits) in + row and column order. It is reasonable to assume that each pixel can be + reasonably well predicted by looking at a few (say 10) neighboring pixels + located above and to the left of the current pixel. Since these 10 pixels + make 1024 combinations, we need 1024 context variables. Each pixel is + encoded using the context variable corresponding to the values of the 10 + neighboring pixels. Each pixel will be decoded by specifying the same + context variable corresponding to the values of these 10 pixels. This is + possible because these 10 pixels (located above and to the left) have + already been decoded and therefore are known by the decoder program. + + The context variables are initially set to zero, which mean that we do not + know yet how to predict the current message bit on the basis of the + context bits. While coding the message bits, the ZP-Coder automatically + estimates the frequencies of #0#s and #1#s coded using each context + variable. These frequencies actually provide a prediction (the most + probable bit value) and an estimation of the prediction reliability (how + often the prediction was correct in the past). All this statistical + information is stored into the context variable after coding each bit. In + other words, the more we code bits within a particular context, the better + the ZP-Coder adapts its prediction model, and the more compression we can + obtain. + + All this adaptation works indeed because both the encoder program and the + decoder program are always synchronized. Both the encoder and the decoder + see the same message bits encoded (or decoded) with the same context + variables. Both the encoder and the decoder apply the same rules to + update the context variables and improve the predictors. Both the encoder + and the decoder programs use the same predictors for any given message + bit. The decoder could not work if this was not the case. + + Just before encoding a message bit, all the context variables in the + encoder program contain certain values. Just before decoding this message + bit, all the context variables in the decoder program must contain the same + values as for the encoder program. This is guaranteed as long as + each prediction only depends on already coded bits: {\em the coding context, + on which the each prediction is based, must be composed of message bits which + have already been coded. } + + {\bf Usage} --- + Once you know how to organize the predictions (i.e. which coding context + to use, how many context variables to initialize, etc.), using the + ZP-Coder is straightforward (see \Ref{ZPCodec Examples}): + \begin{itemize} + \item The {\em encoder program} allocates context variables and + initializes them to zero. It then constructs a \Ref{ZPCodec} object for + encoding. For each message bit, the encoder program retrieves the context + bits, selects a context variable on the basis of the context bits and + calls member function \Ref{ZPCodec::encoder} with the message bit and a + reference to the context variable. + \item The {\em decoder program} allocates context variables and + initializes them to zero. It then constructs a \Ref{ZPCodec} object for + decoding. For each message bit, the decoder program retrieves the context + bits, selects a context variable on the basis of the context bits and + calls member function \Ref{ZPCodec::decoder} with a reference to the + context variable. This function returns the message bit. + \end{itemize} + Functions #encoder# and #decoder# only require a few machine cycles to + perform two essential tasks, namely {\em coding} and {\em context + adaptation}. Function #decoder# often returns after two arithmetic + operations only. To make your program fast, you just need to feed message + bits and context variables fast enough. + + {\bf History} --- The ZP-Coder is similar in function and performance to + the seminal Q-Coder (Pennebaker, Mitchell, Langdon, Arps, IBM J. Res + Dev. 32, 1988). An improved version of the Q-Coder, named QM-Coder, has + been described in certain parts of the JPEG standard. Unfortunate patent + policies have made these coders very difficult to use in general purpose + applications. The Z-Coder is constructed using a new approach based on an + extension of the Golomb codes (Bottou, Howard, Bengio, IEEE DCC 98, 1998 + \URL[DjVu]{http://www.research.att.com/~leonb/DJVU/bottou-howard-bengio/} + \URL[PostScript]{http://www.research.att.com/~leonb/PS/bottou-howard-bengio.ps.gz}) + This new approach does not infringe the QM-Coder patents. Unfortunately + the Z-Coder is dangerously close to the patented Arithmetic MEL Coder. + Therefore we wrote the ZP-Coder (pronounce Zee-Prime Coder) which we + believe is clear of legal problems. Needless to say, AT&T has patents + pending for both the Z-Coder and the ZP-Coder, licenced to LizardTech. + The good news however is that we can grant a license to use the ZP-Coder + in ``free software'' without further complication. See the Copyright + for more information. + + @memo + Binary adaptive quasi-arithmetic coder. + @version + #$Id: ZPCodec.h,v 1.9 2003/11/07 22:08:22 leonb Exp $# + @author + L\'eon Bottou <[email protected]> */ +//@{ + + +/** Context variable. + Variables of type #BitContext# hold a single byte describing how to encode + or decode message bits with similar statistical properties. This single + byte simultaneously represents the current estimate of the bit probability + distribution (which is determined by the frequencies of #1#s and #0#s + already coded with this context) and the confidence in this estimate + (which determines how fast the estimate can change.) + + A coding program typically allocates hundreds of context variables. Each + coding context is initialized to zero before encoding or decoding. Value + zero represents equal probabilities for #1#s and #0#s with a minimal + confidence and therefore a maximum adaptation speed. Each message bit is + encoded using a coding context determined as a function of previously + encoded message bits. The decoder therefore can examine the previously + decoded message bits and decode the current bit using the same context as + the encoder. This is critical for proper decoding. +*/ +typedef unsigned char BitContext; + + +/** Performs ZP-Coder encoding and decoding. A ZPCodec object must either + constructed for encoding or for decoding. The ZPCodec object is connected + with a \Ref{ByteStream} object specified at construction time. A ZPCodec + object constructed for decoding reads code bits from the ByteStream and + returns a message bit whenever function \Ref{decoder} is called. A + ZPCodec constructed for encoding processes the message bits provided by + function \Ref{encoder} and writes the corresponding code bits to + ByteStream #bs#. + + You should never directly access a ByteStream object connected to a valid + ZPCodec object. The most direct way to access the ByteStream object + consists of using the "pass-thru" versions of functions \Ref{encoder} and + \Ref{decoder}. + + The ByteStream object can be accessed again after the destruction of the + ZPCodec object. Note that the encoder always flushes its internal buffers + and writes a few final code bytes when the ZPCodec object is destroyed. + Note also that the decoder often reads a few bytes beyond the last code byte + written by the encoder. This lag means that you must reposition the + ByteStream after the destruction of the ZPCodec object and before re-using + the ByteStream object (see \Ref{IFFByteStream}.) + + Please note also that the decoder has no way to reliably indicate the end + of the message bit sequence. The content of the message must be designed + in a way which indicates when to stop decoding. Simple ways to achieve + this consists of announcing the message length at the beginning (like a + pascal style string), or of defining a termination code (like a null + terminated string). */ + +class ZPCodec : public GPEnabled { +protected: + ZPCodec (GP<ByteStream> gbs, const bool encoding, const bool djvucompat=false); +public: + class Encode; + class Decode; + + /// Non-virtual destructor. + ~ZPCodec(); + /** Constructs a ZP-Coder. If argument #encoding# is zero, the ZP-Coder + object will read code bits from the ByteStream #bs# and return a message + bit whenever function #decoder# is called. If flag #encoding# is set + the ZP-Coder object will process the message bits provided by function + #encoder# and write code bits to ByteStream #bs#. Optional flag + #djvucompat# selects a slightly less efficient adaptation table which is + used by the DjVu project. This is required in order to ensure the + bitstream compatibility. You should not use this flag unless you want + to decode JB2, IW44 or BZZ encoded data. */ + static GP<ZPCodec> create( + GP<ByteStream> gbs, const bool encoding, const bool djvucompat=false); + + /** Encodes bit #bit# using context variable #ctx#. Argument #bit# must be + #0# or #1#. This function should only be used with ZP-Coder objects + created for encoding. It may modify the contents of variable #ctx# in + order to perform context adaptation. */ + void encoder(int bit, BitContext &ctx); + + /** Decodes a bit using context variable #ctx#. This function should only be + used with ZP-Coder objects created for decoding. It may modify the + contents of variable #ctx# in order to perform context adaptation. */ + int decoder(BitContext &ctx); + + /** Encodes bit #bit# without compression (pass-thru encoder). Argument + #bit# must be #0# or #1#. No compression will be applied. Calling this + function always increases the length of the code bit sequence by one + bit. */ + void encoder(int bit); + + /** Decodes a bit without compression (pass-thru decoder). This function + retrieves bits encoded with the pass-thru encoder. */ + int decoder(void); +#ifdef ZPCODEC_BITCOUNT + /** Counter for code bits (requires #-DZPCODEC_BITCOUNT#). This member + variable is available when the ZP-Coder is compiled with option + #-DZPCODEC_BITCOUNT#. Variable #bitcount# counts the number of code + bits processed by the coder since the construction of the object. This + variable can be used to evaluate how many code bits are spent on various + components of the message. */ + int bitcount; +#endif + // Table management (advanced stuff) + struct Table { + unsigned short p; + unsigned short m; + BitContext up; + BitContext dn; + }; + void newtable(ZPCodec::Table *table); + BitContext state(float prob1); + // Non-adaptive encoder/decoder + void encoder_nolearn(int pix, BitContext &ctx); + int decoder_nolearn(BitContext &ctx); + inline int IWdecoder(void); + inline void IWencoder(const bool bit); +protected: + // coder status + GP<ByteStream> gbs; // Where the data goes/comes from + ByteStream *bs; // Where the data goes/comes from + const bool encoding; // Direction (0=decoding, 1=encoding) + unsigned char byte; + unsigned char scount; + unsigned char delay; + unsigned int a; + unsigned int code; + unsigned int fence; + unsigned int subend; + unsigned int buffer; + unsigned int nrun; + // table + unsigned int p[256]; + unsigned int m[256]; + BitContext up[256]; + BitContext dn[256]; + // machine independent ffz + char ffzt[256]; + // encoder private + void einit (void); + void eflush (void); + void outbit(int bit); + void zemit(int b); + void encode_mps(BitContext &ctx, unsigned int z); + void encode_lps(BitContext &ctx, unsigned int z); + void encode_mps_simple(unsigned int z); + void encode_lps_simple(unsigned int z); + void encode_mps_nolearn(unsigned int z); + void encode_lps_nolearn(unsigned int z); + // decoder private + void dinit(void); + void preload(void); + int ffz(unsigned int x); + int decode_sub(BitContext &ctx, unsigned int z); + int decode_sub_simple(int mps, unsigned int z); + int decode_sub_nolearn(int mps, unsigned int z); +private: + // no copy allowed (hate c++) + ZPCodec(const ZPCodec&); + ZPCodec& operator=(const ZPCodec&); +#ifdef ZPCODEC_FRIEND + friend ZPCODEC_FRIEND; +#endif +}; + + + + + + +// INLINE CODE + +inline void +ZPCodec::encoder(int bit, BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (bit != (ctx & 1)) + { + encode_lps(ctx, z); + }else if (z >= 0x8000) + { + encode_mps(ctx, z); + }else + { + a = z; + } +} + +inline int +ZPCodec::IWdecoder(void) +{ + return decode_sub_simple(0,0x8000 + ((a+a+a) >> 3)); +} + +inline int +ZPCodec::decoder(BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (z <= fence) + { a = z; return (ctx&1); } + return decode_sub(ctx, z); +} + +inline void +ZPCodec::encoder_nolearn(int bit, BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (bit != (ctx & 1)) + encode_lps_nolearn(z); + else if (z >= 0x8000) + encode_mps_nolearn(z); + else + a = z; +} + +inline int +ZPCodec::decoder_nolearn(BitContext &ctx) +{ + unsigned int z = a + p[ctx]; + if (z <= fence) + { a = z; return (ctx&1); } + return decode_sub_nolearn( (ctx&1), z); +} + +inline void +ZPCodec::encoder(int bit) +{ + if (bit) + encode_lps_simple(0x8000 + (a>>1)); + else + encode_mps_simple(0x8000 + (a>>1)); +} + +inline int +ZPCodec::decoder(void) +{ + return decode_sub_simple(0, 0x8000 + (a>>1)); +} + +inline void +ZPCodec::IWencoder(const bool bit) +{ + const int z = 0x8000 + ((a+a+a) >> 3); + if (bit) + { + encode_lps_simple(z); + }else + { + encode_mps_simple(z); + } +} + +// ------------ ADDITIONAL DOCUMENTATION + +/** @name ZPCodec Examples + + Binary adaptive coders are efficient and very flexible. Unfortunate + intellectual property issues however have limited their popularity. As a + consequence, few programmers have a direct experience of using such a + coding device. The few examples provided in this section demonstrate how + we think the ZP-Coder should be used. + + {\bf Encoding Multivalued Symbols} --- + Since the ZP-Coder is a strictly binary coder, every message must be + reduced to a sequence of bits (#0#s or #1#s). It is often convenient to + consider that a message is a sequence of symbols taking more than two + values. For instance, a character string may be a sequence of bytes, and + each byte can take 256 values. Each byte of course is composed of eight + bits that we can encode in sequence. The real issue however consists of + deciding how we will use context variables in order to let the ZP-Coder + learn the probability distribution of the byte values. + + The most significant bit #b0# decides whether the byte is in range 0..127 + or in range 128..255. We let the ZP-Coder learn how to predict this bit + by allocating one context variable for it. The second most significant + byte #b1# has two distinct meanings depending of bit #b0#. If bit #b0# is + #0#, bit #b1# decides whether the byte is in range 0..63 or 64..127. If + bit #b0# is #1#, bit #b1# decides whether the byte is in range 128..191 or + 192..255. The prediction for bit #b1# must therefore depend on the value + of #b0#. This is why we will allocate two context variables for this bit. + If bit #b0# is #0#, we will use the first variable; if bit #b0# is #1#, we + will use the second variable. The next bit #b2# has four meanings and + therefore we will use four context variables, etc. This analysis leads to + a total of #1+2+4+...+128# = #255# context variables for encoding one + byte. This encoding procedure can be understood as a binary decision + tree with a dedicated context variable for predicting each decision. + \begin{verbatim} + [>=128]----n---[>=64?]----n----[>31?] ... + \ `---y----[>95?] ... + \ + `--y---[>=192?]----n---[>=160?] ... + `---y---[>=224?] ... + \end{verbatim} + The following decoding function illustrates a very compact way to + implement such a decision tree. Argument #ctx# points to an array of 255 + #BitContext# variables. Macro #REPEAT8# is a shorthand notation for eight + repetitions of its argument. + \begin{verbatim} + int decode_8_bits(ZPCodec &zp, BitContext *ctx ) + { + int n = 1; + REPEAT8( { n = (n<<1) | (zp.decoder(ctx[n-1])); } ); + return n & 0xff; + } + \end{verbatim} + The binary representation of variable #n# is always composed of a #1# + followed by whichever bits have been decoded so far. This extra bit #1# in + fact is a nice trick to flatten out the tree structure and directly + address the array of context variables. Bit #b0# is decoded using the + first context variable since #n# is initially #1#. Bit #b1# is decoded + using one of the next two variables in the array, since #n# is either #2# + (#10# in binary) or #3# (#11# in binary). Bit #b2# will be decoded using + one of the next four variables, since #n# ranges from #4# (#100# in + binary) to #7# (#111# in binary). The final result is given by removing + the extra #1# in variable #n#. + + The corresponding encoding function is almost as compact. Argument #ctx# + again is an array of 255 #BitContext# variables. Each bit of byte #x# is + encoded and shifted into variable #n# as in the decoding function. + Variable #x# in fact contains the bits to be encoded. Variable #n# + contains a #1# followed by the already encoded bits. + \begin{verbatim} + void encode_8_bits(ZPCodec &zp, int x, BitContext *ctx ) + { + int n = 1; + REPEAT8( { int b=((x&0x80)?1:0); x=(x<<1); + zp.encoder(b,ctx[n-1]); n=(n<<1)|(b); } ); + } + \end{verbatim} + The ZP-Coder automatically adjusts the content of the context variables + while coding (recall the context variable argument is passed to functions + #encoder# and #decoder# by reference). The whole array of 255 context + variables can be understood as a "byte context variable". The estimated + probability of each byte value is indeed the product of the estimated + probabilities of the eight binary decisions that lead to that value in the + decision tree. All these probabilities are adapted by the underlying + adaptation algorithm of the ZP-Coder. + + {\bf Application} --- + We consider now a simple applications consisting of encoding the + horizontal and vertical coordinates of a cloud of points. Each coordinate + requires one byte. The following function illustrates a possible + implementation: + \begin{verbatim} + void encode_points(const char *filename, int n, int *x, int *y) + { + StdioByteStream bs(filename, "wb"); + bs.write32(n); // Write number of points. + ZPCodec zp(bs, 1); // Construct encoder and context vars. + BitContext ctxX[255], ctxY[255]; + memset(ctxX, 0, sizeof(ctxX)); + memset(ctxY, 0, sizeof(ctxY)); + for (int i=0; i<n; i++) { // Encode coordinates. + encode_8_bits(zp, x[i], ctxX); + encode_8_bits(zp, y[i], ctxY); + } + } + \end{verbatim} + The decoding function is very similar to the encoding function: + \begin{verbatim} + int decode_points(const char *filename, int *x, int *y) + { + StdioByteStream bs(filename,"rb"); + int n = bs.read32(); // Read number of points. + ZPCodec zp(bs, 0); // Construct decoder and context vars. + BitContext ctxX[255], ctxY[255]; + memset(ctxX, 0, sizeof(ctxX)); + memset(ctxY, 0, sizeof(ctxY)); + for (int i=0; i<n; i++) { // Decode coordinates. + x[i] = decode_8_bits(zp, ctxX); + y[i] = decode_8_bits(zp, ctxY); + } + return n; // Return number of points. + } + \end{verbatim} + The ZP-Coder automatically estimates the probability distributions of both + the horizontal and vertical coordinates. These estimates are used to + efficiently encode the point coordinates. This particular implementation + is a good option if we assume that the order of the points is significant + and that successive points are independent. It would be much smarter + otherwise to sort the points and encode relative displacements between + successive points. + + + {\bf Huffman Coding Tricks} --- + Programmers with experience in Huffman codes can see the similarity in the + ZP-Coder. Huffman codes also organize the symbol values as a decision + tree. The tree is balanced in such a way that each decision is as + unpredictable as possible (i.e. both branches must be equally probable). + This is very close to the ZP-Coder technique described above. Since we + allocate one context variable for each decision, our tree need not be + balanced: the context variable will track the decision statistics and the + ZP-Coder will compensate optimally. + + There are good reasons however to avoid unbalanced trees with the ZP-Coder. + Frequent symbol values may be located quite deep in a poorly balanced + tree. This increases the average number of message bits (the number of + decisions) required to code a symbol. The ZP-Coder will be called more + often, making the coding program slower. Furthermore, each message + bit is encoded using an estimated distribution. All these useless message + bits mean that the ZP-Coder has more distributions to adapt. This + extra adaptation work will probably increase the file size. + + Huffman codes are very fast when the tree structure is fixed beforehand. + Such {\em static Huffman codes} are unfortunately not very efficient + because the tree never matches the actual data distribution. This is why + such programs almost always define a data dependent tree structure. This + structure must then be encoded in the file since the decoder must know it + before decoding the symbols. Static Huffman codes however become very + efficient when decisions are encoded with the ZP-Coder. The tree + structure represents a priori knowledge about the distribution of the + symbol values. Small data discrepancies will be addressed transparently + by the ZP-Coder. + + + {\bf Encoding Numbers} --- + This technique is illustrated with the following number encoding example. + The multivalued technique described above is not practical with large + numbers because the decision tree has too many nodes and requires too many + context variables. This problem can be solved by using a priori knowledge + about the probability distribution of our numbers. + + Assume for instance that the distribution is symmetrical and that small + numbers are much more probable than large numbers. We will first group + our numbers into several sets. Each number is coded by first coding which + set contains the number and then coding a position within the set. Each + set contains #2^n# numbers that we consider roughly equiprobable. Since + the most probable values occur much more often, we want to model their + probability more precisely. Therefore we use small sets for the most + probable values and large sets for the least probable values, as + demonstrated below. + \begin{verbatim} + A---------------- {0} (size=1) + `------B---C---- {1} or {-1} (size=1) + \ `--- {2,3} or {-2,-3} (size=2) + D------ {4...131} or {-4...-131} (size=128) + `----- {132...32899} or {-132...-32899} (size=32768) + \end{verbatim} + We then organize a decision tree for coding the set identifier. This + decision tree is balanced using whatever a priori knowledge we have about + the probability distribution of the number values, just like a static + Huffman tree. Each decision (except the sign decision) is then coded + using a dedicated context variable. + \begin{verbatim} + if (! zp.decoder(ctx_A)) { // decision A + return 0; + } else { + if (! zp.decoder(ctx_B)) { // + decision B + if (! zp.decoder(ctx_C)) { // ++ decision C + if (! zp.decoder()) // +++ sign decision + return +1; + else + return -1; + } else { + if (! zp.decoder()) // +++ sign decision + return + 2 + zp.decoder(); + else + return - 2 - zp.decoder(); + } + } else { + if (! zp.decoder(ctx_D)) { // ++ decision D + if (! zp.decoder()) // +++ sign decision + return + 4 + decode_7_bits(zp); + else + return - 4 - decode_7_bits(zp); + } else { + if (! zp.decoder()) // +++ sign decision + return + 132 + decode_15_bits(zp); + else + return - 132 - decode_15_bits(zp); + } + } + } + \end{verbatim} + Note that the call #zp.decoder()# for coding the sign decision does not use + a context variable. This is a "pass-thru" variant of \Ref{decoder} which + bypasses the ZP-Coder and just reads a bit from the code sequence. There + is a corresponding "pass-thru" version of \Ref{encoder} for encoding such + bits. Similarly, functions #decode_7_bits# and #decode_15_bits# do not + take an array of context variables because, unlike function #decode_8_bits# + listed above, they are based on the pass-thru decoder instead of the + regular decoder. + + The ZP-Coder will not learn the probabilities of the numbers within a set + since no context variables have been allocated for that purpose. This + could be improved by allocating additional context variables for encoding + the position within the smaller sets and using the regular decoding + functions instead of the pass-thru variants. Only experimentation can tell + what works best for your particular encoding problem. + + + {\bf Understanding Adaptation} --- + We have so far explained that the ZP-Coder adaptation algorithm is able to + quickly estimate of the probability distribution of the message bits coded + using a particular context variable. It is also able to track slow + variations when the actual probabilities change while coding. + + Let us consider the ``cloud of points'' application presented above. + Suppose that we first code points located towards the left side and then + slowly move towards points located on the right side. The ZP-Coder will + first estimate that the X coordinates are rather on the left side. This + estimation will be progressively revised after seeing more points on the + right side. Such an ordering of the points obviously violates the point + independence assumption on which our code is based. Despite our inexact + assumptions, the tracking mechanism allows for better prediction of the X + coordinates and therefore better compression. + + However, this is not a perfect solution. The ZP-Coder tracks the changes + because every point seems to be a little bit more on the right side than + suggested by the previous points. The ZP-Coder coding algorithm is always + slightly misadjusted and we always lose a little on possible compression + ratio. This is not much of a problem when the probabilities drift slowly. + On the other hand, this can be very significant if the probabilities change + drastically. + + Adaptation is always associated with a small loss of efficiency. The + ZP-Coder updates the probability model whenever it suspects, {\em after + coding}, that the current settings were not optimal. The model will be + better next time, but a slight loss in compression has occurred. The + design of ZP-Coder of course minimizes this effect as much as possible. + Yet you will pay a price if you ask too much to the adaptation algorithm. + If you have millions of context variables, it will be difficult to train + them all. If the probability distributions change drastically while + coding, it will be difficult to track the changes fast enough. + + Adaptation on the other hand is a great simplification. A good data + compression program must (a) represent the data in order to make its + predictability apparent, and (b) perform the predictions and generate the + code bits. The ZP-Coder is an efficient and effortless solution for + implementing task (b). + + + {\bf Practical Debugging Tricks} --- + Sometimes you write an encoding program and a decoding program. + Unfortunately there is a bug: the decoding program decodes half the file + and then just outputs garbage. There is a simple way to locate the + problem. In the encoding program, after each call to #encoder#, print the + encoded bit and the value of the context variable. In the decoding + program, after each call to #decoder#, print the decoded bit and the value + of the context variable. Both program should print exactly the same thing. + When you find the difference, you find the bug. + + @memo Suggestions for efficiently using the ZP-Coder. */ +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif + + diff --git a/kviewshell/plugins/djvu/libdjvu/configure.in.in b/kviewshell/plugins/djvu/libdjvu/configure.in.in new file mode 100644 index 00000000..c2a1d2b6 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/configure.in.in @@ -0,0 +1,674 @@ +dnl Copyright (c) 2002 Leon Bottou and Yann Le Cun. +dnl Copyright (c) 2001 AT&T +dnl +dnl Most of these macros are derived from macros listed +dnl at the GNU Autoconf Macro Archive +dnl http://www.gnu.org/software/ac-archive/ +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA02111 USA +dnl + +dnl ------------------------------------------------------- +dnl @synopsis AC_CHECK_CXX_OPT(OPTION, +dnl ACTION-IF-OKAY,ACTION-IF-NOT-OKAY) +dnl Check if compiler accepts option OPTION. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CHECK_CXX_OPT],[ + opt="$1" + AC_MSG_CHECKING([if $CXX accepts $opt]) + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + AC_MSG_RESULT(yes) + rm conftest.* + $2 + else + AC_MSG_RESULT(no) + rm conftest.* + $3 + fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_MEMBER_TEMPLATES +dnl If the compiler supports member templates, +dnl define HAVE_MEMBER_TEMPLATES. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_MEMBER_TEMPLATES], +[AC_CACHE_CHECK(whether the compiler supports member templates, +ac_cv_cxx_member_templates, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +template<class T, int N> class A +{ public: + template<int N2> A<T,N> operator=(const A<T,N2>& z) { return A<T,N>(); } +};],[A<double,4> x; A<double,7> y; x = y; return 0;], + ac_cv_cxx_member_templates=yes, ac_cv_cxx_member_templates=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_member_templates" = yes; then + AC_DEFINE(HAVE_MEMBER_TEMPLATES,1, + [define if the compiler supports member templates]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_NAMESPACES +dnl Define HAVE_NAMESPACES if the compiler supports +dnl namespaces. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_NAMESPACES], +[AC_CACHE_CHECK(whether the compiler implements namespaces, +ac_cv_cxx_namespaces, +[ AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([namespace Outer { namespace Inner { int i = 0; }}], + [using namespace Outer::Inner; return i;], + ac_cv_cxx_namespaces=yes, ac_cv_cxx_namespaces=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_namespaces" = yes && test "$ac_debug" = no; then + AC_DEFINE(HAVE_NAMESPACES,1, + [define if the compiler implements namespaces]) +fi +]) + + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_TYPENAME +dnl Define HAVE_TYPENAME if the compiler recognizes +dnl keyword typename. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_TYPENAME], +[AC_CACHE_CHECK(whether the compiler recognizes typename, +ac_cv_cxx_typename, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([template<typename T>class X {public:X(){}};], +[X<float> z; return 0;], + ac_cv_cxx_typename=yes, ac_cv_cxx_typename=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_typename" = yes; then + AC_DEFINE(HAVE_TYPENAME,1,[define if the compiler recognizes typename]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_STDINCLUDES +dnl Define HAVE_STDINCLUDES if the compiler has the +dnl new style include files (without the .h) +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_STDINCLUDES], +[AC_CACHE_CHECK(whether the compiler comes with standard includes, +ac_cv_cxx_stdincludes, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include <new> +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } ],[], + ac_cv_cxx_stdincludes=yes, ac_cv_cxx_stdincludes=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_stdincludes" = yes; then + AC_DEFINE(HAVE_STDINCLUDES,1, + [define if the compiler comes with standard includes]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_BOOL +dnl If the compiler recognizes bool as a separate built-in type, +dnl define HAVE_BOOL. Note that a typedef is not a separate +dnl type since you cannot overload a function such that it +dnl accepts either the basic type or the typedef. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_BOOL], +[AC_CACHE_CHECK(whether the compiler recognizes bool as a built-in type, +ac_cv_cxx_bool, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} +],[bool b = true; return f(b);], + ac_cv_cxx_bool=yes, ac_cv_cxx_bool=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_bool" = yes; then + AC_DEFINE(HAVE_BOOL,1,[define if bool is a built-in type]) +fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_EXCEPTIONS +dnl If the C++ compiler supports exceptions handling (try, +dnl throw and catch), define HAVE_EXCEPTIONS. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_EXCEPTIONS], +[AC_CACHE_CHECK(whether the compiler supports exceptions, +ac_cv_cxx_exceptions, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE(,[try { throw 1; } catch (int i) { return i; }], + ac_cv_cxx_exceptions=yes, ac_cv_cxx_exceptions=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_exceptions" = yes; then + AC_DEFINE(HAVE_EXCEPTIONS,1,[define if the compiler supports exceptions]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_RPO +dnl Defines option --enable-rpo and searches program RPO. +dnl Set output variables CXXRPOFLAGS and RPO. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_RPO], +[ CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + AC_ARG_ENABLE([rpo], + AC_HELP_STRING([--enable-rpo], + [Enable compilation with option -frepo]), + [ac_rpo=$enableval], [ac_rpo=no] ) + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + AC_SUBST(CXXRPOFLAGS) + AC_SUBST(RPO_YES) + AC_SUBST(RPO_NO) +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl This macro figures out how to build C programs using POSIX +dnl threads. It sets the PTHREAD_LIBS output variable to the threads +dnl library and linker flags, and the PTHREAD_CFLAGS output variable +dnl to any special C compiler flags that are needed. (The user can also +dnl force certain compiler flags/libs to be tested by setting these +dnl environment variables.). +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_pthread_ok=no +# First, check if the POSIX threads header, pthread.h, is available. +# If it isn't, don't bother looking for the threads libraries. +AC_CHECK_HEADER(pthread.h, , acx_pthread_ok=noheader) +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works. +if test x${PTHREAD_LIBS+set} = xset || + test x${PTHREAD_CFLAGS+set} = xset ; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([provided PTHREAD_LIBS/PTHREAD_CFLAGS.]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" +fi +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. +acx_pthread_flags="pthreads none -Kthread -kthread lthread + -pthread -pthreads -mthreads pthread + --thread-safe -mt" +# The ordering *is* (sometimes) important. +# Some notes on the individual items follow: +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +case "${host_cpu}-${host_os}" in + *solaris*) + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + *solaris* | alpha*-osf*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" +fi +AC_ARG_VAR(PTHREAD_LIBS, [Flags for linking pthread programs.]) +AC_ARG_VAR(PTHREAD_CFLAGS, [Flags for compiling pthread programs.]) +# execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + AC_DEFINE(HAVE_PTHREAD,1,[Define if pthreads are available]) + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_COTHREADS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Define HAVE_COTHREAD if cothreads can be used. +dnl Define HAVE_COTHREAD_PATCH if cothread libgcc patch is available +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_COTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +acx_cothread=no +if test x$GXX = xyes ; then + AC_MSG_CHECKING([whether cothreads work with ${host_cpu}]) + case ${host_cpu} in + i?86|powerpc*|mips*|alpha*|hppa*) + acx_cothread=yes + ;; + esac + AC_MSG_RESULT($acx_cothread) +fi +if test x$acx_cothread != xno ; then + AC_MSG_CHECKING([whether libgcc contains the cothread patch]) + AC_LANG_PUSH([C++]) + AC_TRY_LINK([extern "C" { void *(*__get_eh_context_ptr)(); + void *__new_eh_context(void); }], + [ __get_eh_context_ptr = &__new_eh_context;], + [acx_cothread_patch=yes], [acx_cothread_patch=no]) + AC_LANG_POP([C++]) + AC_MSG_RESULT($acx_cothread_patch) + if test x$acx_cothread_patch = xno ; then + AC_MSG_CHECKING([if the cothread patch is critical]) + echo 'void foo() { throw "Hello"; }' > conftest.cc + compile="$CXX $CXXFLAGS -c conftest.cc" + check="nm conftest.o | grep sjthrow | cat > conftest.out" + acx_cothread_patch=yes + if AC_TRY_EVAL(compile) && AC_TRY_EVAL(check) ; then + if test -z "`cat conftest.out`" ; then + acx_cothread_patch=no + fi + fi + AC_MSG_RESULT($acx_cothread_patch) + rm conftest.* + if test x$acx_cothread_patch = xyes ; then + acx_cothread=no + AC_MSG_WARN([Cothread cannot work without the patch]) + else + AC_MSG_WARN([Applying the patch is recommended anyway]) + fi + AC_MSG_WARN([See the INSTALL file for more information]) + fi +fi +# Must do. +if test x$acx_cothread = xyes ; then + AC_DEFINE(HAVE_COTHREAD,1, + [Define if cothreads are available.]) + if test x$acx_cothread_patch = xyes ; then + AC_DEFINE(HAVE_COTHREAD_PATCH,1, + [Define if libgcc contains the cothread patch.]) + fi + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_THREADS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process optional option --enable-threads +dnl Check availability of pthreads or cothreads +dnl using AC_PATH_PTHREAD and AC_PATH_COTHREAD. +dnl Set output variable THREADS_LIBS and THREADS_CFLAGS +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_THREADS], [ +AC_ARG_ENABLE(threads, + AC_HELP_STRING([--enable-threads], + [select threading model (default is auto)]), + ac_use_threads=$enableval, ac_use_threads=auto) +ac_threads=no +if test x$ac_use_threads != xno ; then + case x$ac_use_threads in + x|xyes|xauto|xposix|xpthread) + AC_PATH_PTHREAD( + [ ac_threads=pthread + ac_use_threads=pthread + THREAD_LIBS="$PTHREAD_LIBS" + THREAD_CFLAGS="$PTHREAD_CFLAGS -DTHREADMODEL=POSIXTHREADS" + ] ) + ;; + esac + case x$ac_use_threads in + xposix|xpthread) + ;; + x|xyes|xauto|xcothread) + AC_PATH_COTHREAD( + [ ac_threads=cothread + THREAD_CFLAGS="-DTHREADMODEL=COTHREADS" + ] ) + ;; + *) + AC_MSG_ERROR( +[Invalid argument for --enable-threads +Valid arguments are: yes, no, posix, pthread, cothread, auto.]) + ;; + esac +fi +AC_SUBST(THREAD_LIBS) +AC_SUBST(THREAD_CFLAGS) +AC_MSG_CHECKING([threading model]) +AC_MSG_RESULT($ac_threads) +if test $ac_threads != no ; then + AC_MSG_RESULT([setting THREAD_CFLAGS=$THREAD_CFLAGS]) + AC_MSG_RESULT([setting THREAD_LIBS=$THREAD_LIBS]) + ifelse([$1],,:,[$1]) +else + ifelse([$2],,:,[$2]) +fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_TIFF([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-tiff +dnl Search LIBTIFF. Define HAVE_TIFF. +dnl Set output variable TIFF_CFLAGS and TIFF_LIBS +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_TIFF], +[ + AC_ARG_VAR(TIFF_LIBS) + AC_ARG_VAR(TIFF_CFLAGS) + ac_tiff=no + AC_ARG_WITH(tiff, + AC_HELP_STRING([--with-tiff=DIR], + [where libtiff is located]), + [ac_tiff=$withval], [ac_tiff=yes] ) + # Process specification + if test x$ac_tiff = xyes ; then + test x${TIFF_LIBS+set} != xset && TIFF_LIBS="-ltiff" + elif test x$ac_tiff != xno ; then + test x${TIFF_LIBS+set} != xset && TIFF_LIBS="-L$ac_tiff -ltiff" + test x${TIFF_CFLAGS+set} != xset && TIFF_CFLAGS="-I$ac_tiff" + fi + # Try linking + if test x$ac_tiff != xno ; then + AC_MSG_CHECKING([for the libtiff library]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $TIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $TIFF_CFLAGS" + LIBS="$LIBS $TIFF_LIBS" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include <stdio.h> +#include <tiffio.h> +#ifdef __cplusplus +} +#endif ],[ +TIFFOpen(0,0);], + [ac_tiff=yes], [ac_tiff=no] ) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_tiff) + fi + # Finish + if test x$ac_tiff = xno; then + TIFF_CFLAGS= ; TIFF_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_TIFF,1,[Define if you have libtiff.]) + AC_MSG_RESULT([setting TIFF_CFLAGS=$TIFF_CFLAGS]) + AC_MSG_RESULT([setting TIFF_LIBS=$TIFF_LIBS]) + ifelse([$1],,:,[$1]) + fi +]) + +# C++ +AC_LANG(C++) +AC_CXX_BOOL +AC_CXX_EXCEPTIONS +AC_CXX_TYPENAME +AC_CXX_STDINCLUDES +AC_CXX_NAMESPACES +AC_CXX_MEMBER_TEMPLATES +AC_CXX_RPO + +# ---------------------------------------- +# Libraries +# ---------------------------------------- + +AC_CHECK_LIB(m,sqrt) +AC_CHECK_LIB(iconv,libiconv) + +# ---------------------------------------- +# Header Files +# ---------------------------------------- + +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_HEADER_TIME +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(wchar.h wctype.h sys/mman.h iconv.h) +AC_CHECK_HEADERS(stdint.h sys/ipc.h sys/shm.h) + +# ---------------------------------------- +# Types +# ---------------------------------------- + +AC_CHECK_TYPES(wchar_t) +AC_CHECK_TYPES(long long int) +AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) + +# ---------------------------------------- +# Functions +# ---------------------------------------- + +AC_FUNC_MMAP +AC_FUNC_FORK +AC_CHECK_FUNCS(wcrtomb iswspace) +AC_CHECK_FUNCS(putc_unlocked strerror vsnprintf) +AC_CHECK_FUNCS(gethostname iconv strftime getpwuid) + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search TIFF library +AC_PATH_TIFF(, +[ no_tiff=yes + AC_MSG_WARN([Tiff support is disabled]) ]) + +# Search MULTITHREADING library +AC_PATH_THREADS(, +[ no_threads=yes + AC_MSG_WARN([Thread support is disabled]) ]) + +# ---------------------------------------- +# Stuff added to config.h +# ---------------------------------------- + +# Fence +AH_TOP([ +#ifndef DJVU_CONFIG_H +#define DJVU_CONFIG_H +/* config.h: begin */ +]) + +# L18N Macros +AH_BOTTOM([ +/* - Miscellaneous */ +#define UNIX 1 +#define NEED_GNUG_PRAGMAS 0 + +/* - BOOL */ +#if !defined(HAVE_BOOL) && !defined(bool) +#define bool char +#define true 1 +#define false 0 +#endif + +/* - WCHAR etc.*/ +#if ! defined(HAVE_WCHAR_T) +#define HAS_WCHAR 0 +#define HAS_WCTYPE 0 +#define HAS_MBSTATE 0 +#else +#define HAS_WCHAR 1 +#if defined(HAVE_WCTYPE_H) && defined(HAVE_ISWSPACE) +#define HAS_WCTYPE 1 +#endif +#if defined(HAVE_MBSTATE_T) && defined(HAVE_WCRTOMB) +#define HAS_MBSTATE 1 +#endif +#endif +#if defined(HAVE_ICONV_H) && defined(HAVE_ICONV) +#define HAS_ICONV 1 +#else +#define HAS_ICONV 0 +#endif + +/* - I18N MESSAGES HELL */ +#define HAS_CTRL_C_IN_ERR_MSG 1 + +/* - CONTAINERS */ +#ifndef HAVE_MEMBER_TEMPLATES +#define GCONTAINER_NO_MEMBER_TEMPLATES +#endif +#ifndef HAVE_TYPENAME +#define GCONTAINER_NO_TYPENAME +#endif + +/* - COTHREAD */ +#ifdef HAVE_COTHREAD +#ifndef HAVE_COTHREAD_PATCH +#define NO_LIBGCC_HOOKS +#endif +#endif + +/* - JPEG */ +#ifdef HAVE_LIBJPEG +#define NEED_JPEG_DECODER +#endif + +/* - MMAP */ +#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H) +#define HAS_MEMMAP 1 +#else +#define HAS_MEMMAP 0 +#endif + +/* config.h: end */ +#endif +]) diff --git a/kviewshell/plugins/djvu/libdjvu/debug.cpp b/kviewshell/plugins/djvu/libdjvu/debug.cpp new file mode 100644 index 00000000..20c93e50 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/debug.cpp @@ -0,0 +1,299 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: debug.cpp,v 1.14 2005/05/27 14:26:00 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "debug.h" + +#if ( DEBUGLVL > 0 ) + +#include "GThreads.h" +#include "GContainer.h" +#include "GString.h" +#include "GString.h" +#include "ByteStream.h" +#include "GURL.h" + +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +#ifndef UNIX +#ifndef WIN32 +#ifndef macintosh +#define UNIX +#endif +#endif +#endif + +static GCriticalSection debug_lock; +#ifdef RUNTIME_DEBUG_ONLY +static int debug_level = 0; +#else +static int debug_level = DEBUGLVL; +#endif +static int debug_id; +static FILE *debug_file; +static int debug_file_count; + +#if THREADMODEL==NOTHREADS +static DjVuDebug debug_obj; +#else +static GMap<long, DjVuDebug> & +debug_map(void) +{ + static GMap<long, DjVuDebug> xmap; + return xmap; +} +#endif + +DjVuDebug::DjVuDebug() + : block(0), indent(0) +{ + id = debug_id++; +#ifdef UNIX + if (debug_file_count++ == 0 && !debug_file) + set_debug_file(stderr); +#endif +} + +DjVuDebug::~DjVuDebug() +{ +#ifdef UNIX + if (--debug_file_count == 0) + { + if (debug_file && (debug_file != stderr)) + fclose(debug_file); + debug_file = 0; + } +#endif +} + +void +DjVuDebug::format(const char *fmt, ... ) +{ + if (! block) + { + va_list ap; + va_start(ap, fmt); + GUTF8String buffer(fmt,ap); + va_end(ap); + GCriticalSectionLock glock(&debug_lock); + if (debug_file) + { + fprintf(debug_file,"%s", (const char*)buffer); + fflush(debug_file); + } +#ifdef WIN32 + else + { + USES_CONVERSION; + OutputDebugString(A2CT((const char *)buffer)); + } +#endif + } +} + +void +DjVuDebug::set_debug_level(int lvl) +{ + debug_level = lvl; +} + +void +DjVuDebug::set_debug_file(FILE * file) +{ + GCriticalSectionLock glock(&debug_lock); + if (debug_file && (debug_file != stderr)) + fclose(debug_file); + debug_file = file; +} + +void +DjVuDebug::modify_indent(int rindent) +{ + indent += rindent; +} + +DjVuDebug& +DjVuDebug::lock(int lvl, int noindent) +{ + int threads_num=1; + debug_lock.lock(); + // Find Debug object +#if THREADMODEL==NOTHREADS + // Get no-threads debug object + DjVuDebug &dbg = debug_obj; +#else + // Get per-thread debug object + long threadid = (long) GThread::current(); + DjVuDebug &dbg = debug_map()[threadid]; + threads_num=debug_map().size(); +#endif + // Check level + dbg.block = (lvl > debug_level); + // Output thread id and indentation + if (! noindent) + { + if (threads_num>1) + dbg.format("[T%d] ", dbg.id); + int ind = dbg.indent; + char buffer[257]; + memset(buffer,' ', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = 0; + while (ind > (int)sizeof(buffer)-1) + { + dbg.format("%s", buffer); + ind -= sizeof(buffer)-1; + } + if (ind > 0) + { + buffer[ind] = 0; + dbg.format("%s", buffer); + } + } + // Return + return dbg; +} + +void +DjVuDebug::unlock() +{ + debug_lock.unlock(); +} + +#define OP(type, fmt) \ +DjVuDebug& DjVuDebug::operator<<(type arg)\ +{ format(fmt, arg); return *this; } + +DjVuDebug& DjVuDebug::operator<<(bool arg) +{ + format("%s", arg ? "TRUE" : "FALSE"); return *this; +} + +OP(char, "%c") +OP(unsigned char, "%c") +OP(int, "%d") +OP(unsigned int, "%u") +OP(short int, "%hd") +OP(unsigned short int, "%hu") +OP(long, "%ld") +OP(unsigned long, "%lu") +OP(float, "%g") +OP(double, "%g") +OP(const void * const, "0x%08x") + +DjVuDebug& DjVuDebug::operator<<(const char * const ptr) +{ + GUTF8String buffer(ptr?ptr:"(null)"); + if(buffer.length() > 255) + { + buffer=buffer.substr(0,252)+"..."; + } + format("%s", (const char *)buffer); + return *this; +} + +DjVuDebug& DjVuDebug::operator<<(const unsigned char * const ptr) +{ + return operator<<( (const char *) ptr ); +} + +DjVuDebug& DjVuDebug::operator<<(const GUTF8String &ptr) +{ + GUTF8String buffer(ptr); + if(buffer.length() > 255) + buffer=buffer.substr(0,252)+"..."; + format("%s", (const char *)buffer); + return *this; +} + +DjVuDebugIndent::DjVuDebugIndent(int inc) + : inc(inc) +{ + DjVuDebug &dbg = DjVuDebug::lock(0,1); + dbg.modify_indent(inc); + dbg.unlock(); +} + +DjVuDebugIndent::~DjVuDebugIndent() +{ + DjVuDebug &dbg = DjVuDebug::lock(0,1); + dbg.modify_indent(-inc); + dbg.unlock(); +} + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif diff --git a/kviewshell/plugins/djvu/libdjvu/debug.h b/kviewshell/plugins/djvu/libdjvu/debug.h new file mode 100644 index 00000000..c39390a2 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/debug.h @@ -0,0 +1,304 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: debug.h,v 1.12 2005/05/27 14:26:01 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma interface +#endif + +#include <stdio.h> +#ifdef WIN32 +# include <atlbase.h> // USES_CONVERSION, A2CT macro +# include <windows.h> // OutputDebugString +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +# endif +#endif + +/** @name debug.h + + Files #"debug.h"# and #"debug.cpp"# implement means to print debug + messages in a multithread safe way. Message are also marked with a thread + identifier. Under Windows, debug messages are directly sent to the + debugger using the Win32 function #OutputDebugString#. Under Unix, debug + messages are printed on the controlling terminal, preferably using device + #/dev/tty#. + + The preprocessor variable #DEBUGLVL# defines which debug code is going to + be compiled. Selecting #-DDEBUGLVL=0# (the default) disables all + debugging code. Selecting a positive values (e.g. #-DDEBUGLVL=4#) enables + more and more debugging code. + + Message output is controlled by the current debugging level (an integer + between #0# and #DEBUGLVL#). Greater values enable more messages. The + initial debugging level is set to the maximum value. The debugging level + can be changed using macro \Ref{DEBUG_SET_LEVEL}. + + Message indentation can be modified using macro \Ref{DEBUG_MAKE_INDENT}. + Messages are generated by macro \Ref{DEBUG_MSG} or its variants. The + argument of the macro can contain several components separated by operator + #<<#, as demonstrated in the example below: + \begin{verbatim} + DEBUG_MSG("The value of a[" << n << "] is " << a[n] << '\n'); + \end{verbatim} + + One more preprocessor variable #RUNTIME_DEBUG_ONLY# enables compilation + of debug code, but does not enable the debug messages automatically. + In order to see them the program should use \Ref{DEBUG_SET_LEVEL} to + change the level to anything greater than 0. Normally this happens when + user specifies option #-debug# in the command line. Usage of + #RUNTIME_DEBUG_ONLY# implies #DEBUGLVL=1# if not specified otherwise. + + Finally, #-DNO_DEBUG# or #-DNDEBUG# can be used instead of #-DDEBUGLVL=0#, + and #-D_DEBUG# can be used instead of #-DDEBUGLVL=0#. + + {\bf Historical Comment} --- Debug macros are rarely used in the reference + DjVu library because Leon thinks that debugging messages unnecessarily + clutter the code. Debug macros are used everywhere in the plugin code + because Andrew thinks that code without debugging messages is close to + useless. No agreement could be reached. Neither could they agree on + if cluttering header files with huge documentation chunks helps to + improve code readability. + + @memo + Macros for printing debug messages. + @version + #$Id: debug.h,v 1.12 2005/05/27 14:26:01 leonb Exp $# + @author + Andrew Erofeev <[email protected]> -- initial implementation \\ + Leon Bottou <[email protected]> -- cleanups */ +//@{ + +#ifndef DEBUGLVL +# ifdef NDEBUG +# define DEBUGLVL 0 +# endif +#endif +#ifndef DEBUGLVL +# ifdef NO_DEBUG +# define DEBUGLVL 0 +# endif +#endif +#ifndef DEBUGLVL +# ifdef RUNTIME_DEBUG_ONLY +# define DEBUGLVL 1 +# endif +#endif +#ifndef DEBUGLVL +# ifdef _DEBUG +# define DEBUGLVL 1 +# endif +#endif +#ifndef DEBUGLVL +# define DEBUGLVL 0 +#endif + +#if DEBUGLVL <= 0 + +# ifndef NO_DEBUG +# define NO_DEBUG +# endif +# ifndef NDEBUG +# define NDEBUG +# endif +# ifdef _DEBUG +# undef _DEBUG +# endif + +# define DEBUG_MAKE_INDENT(x) +# define DEBUG_SET_LEVEL(level) +# define DEBUG_MSG_LVL(level,x) +# define DEBUG_MSGN_LVL(level,x) + +#else + +# ifdef NO_DEBUG +# undef NO_DEBUG +# endif +# ifdef NDEBUG +# undef NDEBUG +# endif +# ifndef _DEBUG +# define _DEBUG +# endif + +class GUTF8String; + +// ------------ SUPPORT + +class DjVuDebug // DJVU_CLASS +{ +private: + int id; + int block; + int indent; + void format(const char *fmt, ... ); +public: + // construction + DjVuDebug(); + ~DjVuDebug(); + // access + static void set_debug_level(int lvl); + static void set_debug_file(FILE * file); + void modify_indent(int rindent); + static DjVuDebug& lock(int lvl, int noindent); + void unlock(); + // printing + DjVuDebug & operator<<(bool b); + DjVuDebug & operator<<(char c); + DjVuDebug & operator<<(unsigned char c); + DjVuDebug & operator<<(int i); + DjVuDebug & operator<<(unsigned int i); + DjVuDebug & operator<<(short int i); + DjVuDebug & operator<<(unsigned short int i); + DjVuDebug & operator<<(long i); + DjVuDebug & operator<<(unsigned long i); + DjVuDebug & operator<<(const char * const ptr); + DjVuDebug & operator<<(const unsigned char * const ptr); + DjVuDebug& operator<<(const GUTF8String &ptr); + DjVuDebug & operator<<(float f); + DjVuDebug & operator<<(double d); + DjVuDebug & operator<<(const void * const p); +}; + +class DjVuDebugIndent // DJVU_CLASS +{ +private: + int inc; +public: + DjVuDebugIndent(int inc=2); + ~DjVuDebugIndent(); +//#define DEBUG_MAKE_INDENT_2(x, y) DjVuDebugIndent debug_indent ## y (x) +//#define DEBUG_MAKE_INDENT_1(x, y) DEBUG_MAKE_INDENT_2(x, y) +#define DEBUG_MAKE_INDENT_1(x, y) DjVuDebugIndent debug_indent ## y (x) +}; + +// ------------ MAIN MACROS + +# define DEBUG_MAKE_INDENT(x) DEBUG_MAKE_INDENT_1(x, __LINE__) +# define DEBUG_SET_LEVEL(level) DjVuDebug::set_debug_level(level) +# define DEBUG_MSG_LVL(level,x) { ( DjVuDebug::lock(level,0) << x ).unlock(); } +# define DEBUG_MSGN_LVL(level,x) { ( DjVuDebug::lock(level,1) << x ).unlock(); } +# define DEBUG_MSGF_LVL(level,x) { ( DjVuDebug::lock(level,1) << x ).unlock(); } + +#endif + + +// ------------ EAF MACROS + +#if ( DEBUGLVL >= 1 ) +/** Generates a level 1 message */ +# define DEBUG1_MSG(x) DEBUG_MSG_LVL(1,x) +# define DEBUG1_MSGF(x) DEBUG_MSGF_LVL(1,x) +#else +# define DEBUG1_MSG(x) +# define DEBUG1_MSGF(x) +#endif +#if ( DEBUGLVL >= 2 ) +/** Generates a level 2 message */ +# define DEBUG2_MSG(x) DEBUG_MSG_LVL(2,x) +# define DEBUG2_MSGF(x) DEBUG_MSGF_LVL(2,x) +#else +# define DEBUG2_MSG(x) +# define DEBUG2_MSGF(x) +#endif +#if ( DEBUGLVL >= 3 ) +/** Generates a level 3 message */ +# define DEBUG3_MSG(x) DEBUG_MSG_LVL(3,x) +# define DEBUG3_MSGF(x) DEBUG_MSGF_LVL(3,x) +#else +# define DEBUG3_MSG(x) +# define DEBUG3_MSGF(x) +#endif +#if ( DEBUGLVL >= 4 ) +/** Generates a level 4 message */ +# define DEBUG4_MSG(x) DEBUG_MSG_LVL(4,x) +# define DEBUG4_MSGF(x) DEBUG_MSGF_LVL(4,x) +#else +# define DEBUG4_MSG(x) +# define DEBUG4_MSGF(x) +#endif + +#define DEBUG_RUNTIME_SET_LEVEL(level) DEBUG_SET_LEVEL(level) +/** Generates a level 1 message. */ +#define DEBUG_MSG(x) DEBUG1_MSG(x) +/** Generates a level 1 message without indentation. */ +#define DEBUG_MSGF(x) DEBUG1_MSGF(x) +/** Generates a level 1 message terminated with a newline. */ +#define DEBUG_MSGN(x) DEBUG_MSG(x<<'\n') + +//@} + +// ------------ THE END + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + +#endif // DEBUG_H |