summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit47d455dd55be855e4cc691c32f687f723d9247ee (patch)
tree52e236aaa2576bdb3840ebede26619692fed6d7d /kviewshell/plugins/djvu/libdjvu
downloadtdegraphics-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')
-rw-r--r--kviewshell/plugins/djvu/libdjvu/Arrays.cpp305
-rw-r--r--kviewshell/plugins/djvu/libdjvu/Arrays.h997
-rw-r--r--kviewshell/plugins/djvu/libdjvu/BSByteStream.cpp465
-rw-r--r--kviewshell/plugins/djvu/libdjvu/BSByteStream.h275
-rw-r--r--kviewshell/plugins/djvu/libdjvu/BSEncodeByteStream.cpp1010
-rw-r--r--kviewshell/plugins/djvu/libdjvu/ByteStream.cpp1445
-rw-r--r--kviewshell/plugins/djvu/libdjvu/ByteStream.h416
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DataPool.cpp1837
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DataPool.h627
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDir.cpp839
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDir.h451
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDir0.cpp169
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDir0.h217
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDoc.cpp663
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmDoc.h274
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmNav.cpp338
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVmNav.h146
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp1514
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuAnno.h295
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.cpp2193
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDocEditor.h460
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDocument.cpp1845
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDocument.h1071
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.cpp353
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuDumpHelper.h126
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuErrorList.cpp174
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuErrorList.h194
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuFile.cpp2831
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuFile.h848
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuFileCache.cpp272
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuFileCache.h292
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuGlobal.cpp255
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuGlobal.h398
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuGlobalMemory.cpp306
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuImage.cpp1486
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuImage.h449
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuInfo.cpp205
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuInfo.h193
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuMessage.cpp647
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuMessage.h135
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.cpp478
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuMessageLite.h227
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuNavDir.cpp237
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuNavDir.h192
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuPalette.cpp590
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuPalette.h339
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuPort.cpp710
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuPort.h518
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuText.cpp971
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuText.h281
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp2582
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuToPS.h425
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GBitmap.cpp1658
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GBitmap.h673
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GContainer.cpp802
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GContainer.h1366
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GException.cpp284
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GException.h356
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GIFFManager.cpp663
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GIFFManager.h394
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GMapAreas.cpp1066
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GMapAreas.h565
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GOS.cpp370
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GOS.h161
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GPixmap.cpp1676
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GPixmap.h531
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GRect.cpp458
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GRect.h407
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GScaler.cpp706
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GScaler.h321
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GSmartPointer.cpp256
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GSmartPointer.h489
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GString.cpp2811
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GString.h1676
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GThreads.cpp1887
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GThreads.h639
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GURL.cpp1965
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GURL.h446
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GUnicode.cpp790
-rw-r--r--kviewshell/plugins/djvu/libdjvu/IFFByteStream.cpp558
-rw-r--r--kviewshell/plugins/djvu/libdjvu/IFFByteStream.h312
-rw-r--r--kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp1797
-rw-r--r--kviewshell/plugins/djvu/libdjvu/IW44Image.cpp1935
-rw-r--r--kviewshell/plugins/djvu/libdjvu/IW44Image.h761
-rw-r--r--kviewshell/plugins/djvu/libdjvu/JB2EncodeCodec.cpp564
-rw-r--r--kviewshell/plugins/djvu/libdjvu/JB2Image.cpp1427
-rw-r--r--kviewshell/plugins/djvu/libdjvu/JB2Image.h805
-rw-r--r--kviewshell/plugins/djvu/libdjvu/JPEGDecoder.cpp413
-rw-r--r--kviewshell/plugins/djvu/libdjvu/JPEGDecoder.h133
-rw-r--r--kviewshell/plugins/djvu/libdjvu/MMRDecoder.cpp961
-rw-r--r--kviewshell/plugins/djvu/libdjvu/MMRDecoder.h238
-rw-r--r--kviewshell/plugins/djvu/libdjvu/MMX.cpp209
-rw-r--r--kviewshell/plugins/djvu/libdjvu/MMX.h194
-rw-r--r--kviewshell/plugins/djvu/libdjvu/Makefile.am18
-rw-r--r--kviewshell/plugins/djvu/libdjvu/README.djvulibre85
-rw-r--r--kviewshell/plugins/djvu/libdjvu/Template.h258
-rw-r--r--kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.cpp368
-rw-r--r--kviewshell/plugins/djvu/libdjvu/UnicodeByteStream.h199
-rw-r--r--kviewshell/plugins/djvu/libdjvu/XMLParser.cpp1128
-rw-r--r--kviewshell/plugins/djvu/libdjvu/XMLParser.h123
-rw-r--r--kviewshell/plugins/djvu/libdjvu/XMLTags.cpp417
-rw-r--r--kviewshell/plugins/djvu/libdjvu/XMLTags.h242
-rw-r--r--kviewshell/plugins/djvu/libdjvu/ZPCodec.cpp1292
-rw-r--r--kviewshell/plugins/djvu/libdjvu/ZPCodec.h747
-rw-r--r--kviewshell/plugins/djvu/libdjvu/configure.in.in674
-rw-r--r--kviewshell/plugins/djvu/libdjvu/debug.cpp299
-rw-r--r--kviewshell/plugins/djvu/libdjvu/debug.h304
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:&#10;&#09;&apos;%1!s!&apos;.";
+
+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:
+
+ &#10; [line feed]
+ &#09; [horizontal tab]
+ &apos; [single quote]
+ &#34; [double quote]
+ &lt; [less than sign]
+ &gt; [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:
+// '<' --> "&lt;"
+// '>' --> "&gt;"
+// '&' --> "&amp;"
+// '\'' --> "&apos;"
+// '\"' --> "&quot;"
+// 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="&lt;";
+ break;
+ case '>':
+ ss="&gt;";
+ break;
+ case '&':
+ ss="&amp;";
+ break;
+ case '\47':
+ ss="&apos;";
+ break;
+ case '\42':
+ ss="&quot;";
+ 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 "&lt;", '>' to "&gt;", '&' to "&amp;" '\'' to
+ "&apos;", and '\"' to "&quot;". 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 "&lt;", '>' to "&gt;", '&' to "&amp;" '\'' to
+ "&apos;", and '\"' to "&quot;". 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., "&#38;" or "&#x26;" for "*") are the only forms
+ converted by this function. */
+ GUTF8String fromEscaped( void ) const;
+
+ /** Converts strings containing HTML/XML escaped characters
+ (e.g., "&lt;" 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., "&#38;" or "&#x26;"
+ 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 "&lt;", '>' to "&gt;", '&' to "&amp;" '\'' to
+ "&apos;", and '\"' to "&quot;". 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, &copyr, &copyw, &copye, &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(&regs,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 &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