• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kviewshell

DjVuFile.cpp

Go to the documentation of this file.
00001 //C-  -*- C++ -*-
00002 //C- -------------------------------------------------------------------
00003 //C- DjVuLibre-3.5
00004 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
00005 //C- Copyright (c) 2001  AT&T
00006 //C-
00007 //C- This software is subject to, and may be distributed under, the
00008 //C- GNU General Public License, Version 2. The license should have
00009 //C- accompanied the software or you may obtain a copy of the license
00010 //C- from the Free Software Foundation at http://www.fsf.org .
00011 //C-
00012 //C- This program is distributed in the hope that it will be useful,
00013 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //C- GNU General Public License for more details.
00016 //C- 
00017 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
00018 //C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech 
00019 //C- Software authorized us to replace the original DjVu(r) Reference 
00020 //C- Library notice by the following text (see doc/lizard2002.djvu):
00021 //C-
00022 //C-  ------------------------------------------------------------------
00023 //C- | DjVu (r) Reference Library (v. 3.5)
00024 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
00025 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
00026 //C- | 6,058,214 and patents pending.
00027 //C- |
00028 //C- | This software is subject to, and may be distributed under, the
00029 //C- | GNU General Public License, Version 2. The license should have
00030 //C- | accompanied the software or you may obtain a copy of the license
00031 //C- | from the Free Software Foundation at http://www.fsf.org .
00032 //C- |
00033 //C- | The computer code originally released by LizardTech under this
00034 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
00035 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
00036 //C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
00037 //C- | non-exclusive license to make, use, sell, or otherwise dispose of 
00038 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
00039 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
00040 //C- | General Public License.   This grant only confers the right to 
00041 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
00042 //C- | the extent such infringement is reasonably necessary to enable 
00043 //C- | recipient to make, have made, practice, sell, or otherwise dispose 
00044 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
00045 //C- | any greater extent that may be necessary to utilize further 
00046 //C- | modifications or combinations.
00047 //C- |
00048 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
00049 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
00050 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
00051 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
00052 //C- +------------------------------------------------------------------
00053 // 
00054 // $Id: DjVuFile.cpp,v 1.11 2003/11/07 22:08:20 leonb Exp $
00055 // $Name: release_3_5_15 $
00056 
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063 
00064 #include "DjVuFile.h"
00065 #include "IFFByteStream.h"
00066 #include "GOS.h"
00067 #include "MMRDecoder.h"
00068 #ifdef NEED_JPEG_DECODER
00069 #include "JPEGDecoder.h"
00070 #endif
00071 #include "DjVuAnno.h"
00072 #include "DjVuText.h"
00073 #include "DataPool.h"
00074 #include "JB2Image.h"
00075 #include "IW44Image.h"
00076 #include "DjVuNavDir.h"
00077 #ifndef NEED_DECODER_ONLY
00078 #include "BSByteStream.h"
00079 #endif // NEED_DECODER_ONLY
00080 
00081 #include "debug.h"
00082 
00083 
00084 #ifdef HAVE_NAMESPACES
00085 namespace DJVU {
00086 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00087 }
00088 #endif
00089 #endif
00090 
00091 
00092 #define STRINGIFY(x) STRINGIFY_(x)
00093 #define STRINGIFY_(x) #x
00094 
00095 
00096 #define REPORT_EOF(x) \
00097   {G_TRY{G_THROW( ByteStream::EndOfFile );}G_CATCH(ex){report_error(ex,(x));}G_ENDCATCH;}
00098 
00099 static GP<GPixmap> (*djvu_decode_codec)(ByteStream &bs)=0;
00100 
00101 class ProgressByteStream : public ByteStream
00102 {
00103 public:
00104   ProgressByteStream(const GP<ByteStream> & xstr) : str(xstr),
00105     last_call_pos(0) {}
00106   virtual ~ProgressByteStream() {}
00107 
00108   virtual size_t read(void *buffer, size_t size)
00109   {
00110     int rc=0;
00111        // G_TRY {} CATCH; block here is merely to avoid egcs internal error
00112     G_TRY {
00113       int cur_pos=str->tell();
00114       if (progress_cb && (last_call_pos/256!=cur_pos/256))
00115       {
00116         progress_cb(cur_pos, progress_cl_data);
00117         last_call_pos=cur_pos;
00118       }
00119       rc=str->read(buffer, size);
00120     } G_CATCH_ALL {
00121       G_RETHROW;
00122     } G_ENDCATCH;
00123     return rc;
00124   }
00125   virtual size_t write(const void *buffer, size_t size)
00126   {
00127     return str->write(buffer, size);
00128   }
00129   virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false)
00130   {
00131     return str->seek(offset, whence);
00132   }
00133   virtual long tell(void ) const { return str->tell(); }
00134 
00135   void      set_progress_cb(void (* xprogress_cb)(int, void *),
00136   void * xprogress_cl_data)
00137   {
00138     progress_cb=xprogress_cb;
00139     progress_cl_data=xprogress_cl_data;
00140   }
00141 private:
00142   GP<ByteStream> str;
00143   void      * progress_cl_data;
00144   void      (* progress_cb)(int pos, void *);
00145   int       last_call_pos;
00146   
00147   // Cancel C++ default stuff
00148   ProgressByteStream & operator=(const ProgressByteStream &);
00149 };
00150 
00151 
00152 DjVuFile::DjVuFile()
00153 : file_size(0), recover_errors(ABORT), verbose_eof(false), chunks_number(-1),
00154 initialized(false)
00155 {
00156 }
00157 
00158 void
00159 DjVuFile::check() const
00160 {
00161   if (!initialized)
00162     G_THROW( ERR_MSG("DjVuFile.not_init") );
00163 }
00164 
00165 GP<DjVuFile>
00166 DjVuFile::create(
00167   const GP<ByteStream> & str, const ErrorRecoveryAction recover_errors,
00168   const bool verbose_eof )
00169 {
00170   DjVuFile *file=new DjVuFile();
00171   GP<DjVuFile> retval=file;
00172   file->set_recover_errors(recover_errors);
00173   file->set_verbose_eof(verbose_eof);
00174   file->init(str);
00175   return retval;
00176 }
00177 
00178 void 
00179 DjVuFile::init(const GP<ByteStream> & str)
00180 {
00181   DEBUG_MSG("DjVuFile::DjVuFile(): ByteStream constructor\n");
00182   DEBUG_MAKE_INDENT(3);
00183   
00184   if (initialized)
00185     G_THROW( ERR_MSG("DjVuFile.2nd_init") );
00186   if (!get_count())
00187     G_THROW( ERR_MSG("DjVuFile.not_secured") );
00188   
00189   file_size=0;
00190   decode_thread=0;
00191   
00192   // Read the data from the stream
00193   data_pool=DataPool::create(str);
00194   
00195   // Construct some dummy URL
00196   GUTF8String buffer;
00197   buffer.format("djvufile:/%p.djvu", this);
00198   DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)buffer<<"\n");
00199   url=GURL::UTF8(buffer);
00200   
00201   // Set it here because trigger will call other DjVuFile's functions
00202   initialized=true;
00203   
00204   // Add (basically - call) the trigger
00205   data_pool->add_trigger(-1, static_trigger_cb, this);
00206 }
00207 
00208 GP<DjVuFile>
00209 DjVuFile::create(
00210   const GURL & xurl, GP<DjVuPort> port, 
00211   const ErrorRecoveryAction recover_errors, const bool verbose_eof ) 
00212 {
00213   DjVuFile *file=new DjVuFile();
00214   GP<DjVuFile> retval=file;
00215   file->set_recover_errors(recover_errors);
00216   file->set_verbose_eof(verbose_eof);
00217   file->init(xurl,port);
00218   return retval;
00219 }
00220 
00221 void
00222 DjVuFile::init(const GURL & xurl, GP<DjVuPort> port) 
00223 {
00224   DEBUG_MSG("DjVuFile::init(): url='" << xurl << "'\n");
00225   DEBUG_MAKE_INDENT(3);
00226   
00227   if (initialized)
00228     G_THROW( ERR_MSG("DjVuFile.2nd_init") );
00229   if (!get_count())
00230     G_THROW( ERR_MSG("DjVuFile.not_secured") );
00231   if (xurl.is_empty())
00232     G_THROW( ERR_MSG("DjVuFile.empty_URL") );
00233   
00234   url = xurl;
00235   DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)url<<"\n");
00236   file_size=0;
00237   decode_thread=0;
00238   
00239   DjVuPortcaster * pcaster=get_portcaster();
00240   
00241   // We need it 'cause we're waiting for our own termination in stop_decode()
00242   pcaster->add_route(this, this);
00243   if (!port)
00244     port = simple_port = new DjVuSimplePort();
00245   pcaster->add_route(this, port);
00246   
00247   // Set it here because trigger will call other DjVuFile's functions
00248   initialized=true;
00249   
00250   if (!(data_pool=DataPool::create(pcaster->request_data(this, url))))
00251     G_THROW( ERR_MSG("DjVuFile.no_data") "\t"+url.get_string());
00252   data_pool->add_trigger(-1, static_trigger_cb, this);
00253 }
00254 
00255 DjVuFile::~DjVuFile(void)
00256 {
00257   DEBUG_MSG("DjVuFile::~DjVuFile(): destroying...\n");
00258   DEBUG_MAKE_INDENT(3);
00259   
00260   // No more messages. They may result in adding this file to a cache
00261   // which will be very-very bad as we're being destroyed
00262   get_portcaster()->del_port(this);
00263   
00264   // Unregister the trigger (we don't want it to be called and attempt
00265   // to access the destroyed object)
00266   if (data_pool)
00267     data_pool->del_trigger(static_trigger_cb, this);
00268   
00269   // We don't have to wait for decoding to finish here. It's already
00270   // finished (we know it because there is a "life saver" in the
00271   // thread function)  -- but we need to delete it
00272   delete decode_thread; decode_thread=0;
00273 }
00274 
00275 void
00276 DjVuFile::reset(void)
00277 {
00278    flags.enter();
00279    info = 0; 
00280    anno = 0; 
00281    text = 0; 
00282    meta = 0; 
00283    bg44 = 0; 
00284    fgbc = 0;
00285    fgjb = 0; 
00286    fgjd = 0;
00287    fgpm = 0;
00288    dir  = 0; 
00289    description = ""; 
00290    mimetype = "";
00291    flags=(flags&(ALL_DATA_PRESENT|DECODE_STOPPED|DECODE_FAILED));
00292    flags.leave();
00293 }
00294 
00295 unsigned int
00296 DjVuFile::get_memory_usage(void) const
00297 {
00298    unsigned int size=sizeof(*this);
00299    if (info) size+=info->get_memory_usage();
00300    if (bg44) size+=bg44->get_memory_usage();
00301    if (fgjb) size+=fgjb->get_memory_usage();
00302    if (fgpm) size+=fgpm->get_memory_usage();
00303    if (fgbc) size+=fgbc->size()*sizeof(int);
00304    if (anno) size+=anno->size();
00305    if (meta) size+=meta->size();
00306    if (dir) size+=dir->get_memory_usage();
00307    return size;
00308 }
00309 
00310 GPList<DjVuFile>
00311 DjVuFile::get_included_files(bool only_created)
00312 {
00313   check();
00314   if (!only_created && !are_incl_files_created())
00315     process_incl_chunks();
00316   
00317   GCriticalSectionLock lock(&inc_files_lock);
00318   GPList<DjVuFile> list=inc_files_list; // Get a copy when locked
00319   return list;
00320 }
00321 
00322 void
00323 DjVuFile::wait_for_chunk(void)
00324 // Will return after a chunk has been decoded
00325 {
00326   check();
00327   DEBUG_MSG("DjVuFile::wait_for_chunk() called\n");
00328   DEBUG_MAKE_INDENT(3);
00329   chunk_mon.enter();
00330   chunk_mon.wait();
00331   chunk_mon.leave();
00332 }
00333 
00334 bool
00335 DjVuFile::wait_for_finish(bool self)
00336 // if self==TRUE, will block until decoding of this file is over
00337 // if self==FALSE, will block until decoding of a child (direct
00338 // or indirect) is over.
00339 // Will return FALSE if there is nothing to wait for. TRUE otherwise
00340 {
00341   DEBUG_MSG("DjVuFile::wait_for_finish():  self=" << self <<"\n");
00342   DEBUG_MAKE_INDENT(3);
00343   
00344   check();
00345   
00346   if (self)
00347   {
00348     // It's best to check for self termination using flags. The reason
00349     // is that finish_mon is updated in a DjVuPort function, which
00350     // will not be called if the object is being destroyed
00351     GMonitorLock lock(&flags);
00352     if (is_decoding())
00353     {
00354       while(is_decoding()) flags.wait();
00355       DEBUG_MSG("got it\n");
00356       return 1;
00357     }
00358   } else
00359   {
00360     // By locking the monitor, we guarantee that situation doesn't change
00361     // between the moments when we check for pending finish events
00362     // and when we actually run wait(). If we don't lock, the last child
00363     // may terminate in between, and we'll wait forever.
00364     //
00365     // Locking is required by GMonitor interface too, btw.
00366     GMonitorLock lock(&finish_mon);
00367     GP<DjVuFile> file;
00368     {
00369       GCriticalSectionLock lock(&inc_files_lock);
00370       for(GPosition pos=inc_files_list;pos;++pos)
00371       {
00372         GP<DjVuFile> & f=inc_files_list[pos];
00373         if (f->is_decoding())
00374         {
00375           file=f; break;
00376         }
00377       }
00378     }
00379     if (file)
00380     {
00381       finish_mon.wait();
00382       DEBUG_MSG("got it\n");
00383       return 1;
00384     }
00385   }
00386   DEBUG_MSG("nothing to wait for\n");
00387   return 0;
00388 }
00389 
00390 void
00391 DjVuFile::notify_chunk_done(const DjVuPort *, const GUTF8String &)
00392 {
00393   check();
00394   chunk_mon.enter();
00395   chunk_mon.broadcast();
00396   chunk_mon.leave();
00397 }
00398 
00399 void
00400 DjVuFile::notify_file_flags_changed(const DjVuFile * src,
00401                                     long set_mask, long clr_mask)
00402 {
00403   check();
00404   if (set_mask & (DECODE_OK | DECODE_FAILED | DECODE_STOPPED))
00405   {
00406     // Signal threads waiting for file termination
00407     finish_mon.enter();
00408     finish_mon.broadcast();
00409     finish_mon.leave();
00410     
00411     // In case a thread is still waiting for a chunk
00412     chunk_mon.enter();
00413     chunk_mon.broadcast();
00414     chunk_mon.leave();
00415   }
00416   
00417   if ((set_mask & ALL_DATA_PRESENT) && src!=this &&
00418     are_incl_files_created() && is_data_present())
00419   {
00420     if (src!=this && are_incl_files_created() && is_data_present())
00421     {
00422       // Check if all children have data
00423       bool all=true;
00424       {
00425         GCriticalSectionLock lock(&inc_files_lock);
00426         for(GPosition pos=inc_files_list;pos;++pos)
00427           if (!inc_files_list[pos]->is_all_data_present())
00428           {
00429             all=false;
00430             break;
00431           }
00432       }
00433       if (all)
00434       {
00435         DEBUG_MSG("Just got ALL data for '" << url << "'\n");
00436         flags|=ALL_DATA_PRESENT;
00437         get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
00438       }
00439     }
00440   }
00441 }
00442 
00443 void
00444 DjVuFile::static_decode_func(void * cl_data)
00445 {
00446   DjVuFile * th=(DjVuFile *) cl_data;
00447   
00448   /* Please do not undo this life saver. If you do then try to resolve the
00449   following conflict first:
00450   1. Decoding starts and there is only one external reference
00451   to the DjVuFile.
00452   2. Decoding proceeds and calls DjVuPortcaster::notify_error(),
00453   which creates inside a temporary GP<DjVuFile>.
00454   3. While notify_error() is running, the only external reference
00455   is lost, but the DjVuFile is still alive (remember the
00456   temporary GP<>?)
00457   4. The notify_error() returns, the temporary GP<> gets destroyed
00458   and the DjVuFile is attempting to destroy right in the middle
00459   of the decoding thread. This is either a dead block (waiting
00460   for the termination of the decoding from the ~DjVuFile() called
00461   from the decoding thread) or coredump. */
00462   GP<DjVuFile> life_saver=th;
00463   th->decode_life_saver=0;
00464   G_TRY {
00465     th->decode_func();
00466   } G_CATCH_ALL {
00467   } G_ENDCATCH;
00468 }
00469 
00470 void
00471 DjVuFile::decode_func(void)
00472 {
00473   check();
00474   DEBUG_MSG("DjVuFile::decode_func() called, url='" << url << "'\n");
00475   DEBUG_MAKE_INDENT(3);
00476   
00477   DjVuPortcaster * pcaster=get_portcaster();
00478   
00479   G_TRY {
00480     const GP<ByteStream> decode_stream(decode_data_pool->get_stream());
00481     ProgressByteStream *pstr=new ProgressByteStream(decode_stream);
00482     const GP<ByteStream> gpstr(pstr);
00483     pstr->set_progress_cb(progress_cb, this);
00484     
00485     decode(gpstr);
00486     
00487     // Wait for all child files to finish
00488     while(wait_for_finish(0))
00489         continue;
00490     
00491     DEBUG_MSG("waiting for children termination\n");
00492     // Check for termination status
00493     GCriticalSectionLock lock(&inc_files_lock);
00494     for(GPosition pos=inc_files_list;pos;++pos)
00495     {
00496       GP<DjVuFile> & f=inc_files_list[pos];
00497       if (f->is_decode_failed())
00498         G_THROW( ERR_MSG("DjVuFile.decode_fail") );
00499       if (f->is_decode_stopped())
00500         G_THROW( DataPool::Stop );
00501       if (!f->is_decode_ok())
00502       {
00503         DEBUG_MSG("this_url='" << url << "'\n");
00504         DEBUG_MSG("incl_url='" << f->get_url() << "'\n");
00505         DEBUG_MSG("decoding=" << f->is_decoding() << "\n");
00506         DEBUG_MSG("status='" << f->get_flags() << "\n");
00507         G_THROW( ERR_MSG("DjVuFile.not_finished") );
00508       }
00509     }
00510   } G_CATCH(exc) {
00511     G_TRY {
00512       if (!exc.cmp_cause(DataPool::Stop))
00513       {
00514         flags.enter();
00515         flags=flags & ~DECODING | DECODE_STOPPED;
00516         flags.leave();
00517         pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.stopped"))
00518                                + GUTF8String("\t") + GUTF8String(url));
00519         pcaster->notify_file_flags_changed(this, DECODE_STOPPED, DECODING);
00520       } else
00521       {
00522         flags.enter();
00523         flags=flags & ~DECODING | DECODE_FAILED;
00524         flags.leave();
00525         pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.failed"))
00526                                + GUTF8String("\t") + GUTF8String(url));
00527         pcaster->notify_error(this, exc.get_cause());
00528         pcaster->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
00529       }
00530     } G_CATCH_ALL
00531     {
00532       DEBUG_MSG("******* Oops. Almost missed an exception\n");
00533     } G_ENDCATCH;
00534   } G_ENDCATCH;
00535   
00536   decode_data_pool->clear_stream();
00537   G_TRY {
00538     if (flags.test_and_modify(DECODING, 0, DECODE_OK | INCL_FILES_CREATED, DECODING))
00539       pcaster->notify_file_flags_changed(this, DECODE_OK | INCL_FILES_CREATED, 
00540                                          DECODING);
00541   } G_CATCH_ALL {} G_ENDCATCH;
00542   DEBUG_MSG("decoding thread for url='" << url << "' ended\n");
00543 }
00544 
00545 GP<DjVuFile>
00546 DjVuFile::process_incl_chunk(ByteStream & str, int file_num)
00547 {
00548   check();
00549   DEBUG_MSG("DjVuFile::process_incl_chunk(): processing INCL chunk...\n");
00550   DEBUG_MAKE_INDENT(3);
00551   
00552   DjVuPortcaster * pcaster=get_portcaster();
00553   
00554   GUTF8String incl_str;
00555   char buffer[1024];
00556   int length;
00557   while((length=str.read(buffer, 1024)))
00558     incl_str+=GUTF8String(buffer, length);
00559   
00560   // Eat '\n' in the beginning and at the end
00561   while(incl_str.length() && incl_str[0]=='\n')
00562   {
00563     incl_str=incl_str.substr(1,(unsigned int)(-1));
00564   }
00565   while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
00566   {
00567     incl_str.setat(incl_str.length()-1, 0);
00568   }
00569   
00570   if (incl_str.length()>0)
00571   {
00572     if (strchr(incl_str, '/'))
00573       G_THROW( ERR_MSG("DjVuFile.malformed") );
00574     
00575     DEBUG_MSG("incl_str='" << incl_str << "'\n");
00576     
00577     GURL incl_url=pcaster->id_to_url(this, incl_str);
00578     if (incl_url.is_empty())    // Fallback. Should never be used.
00579       incl_url=GURL::UTF8(incl_str,url.base());
00580     
00581     // Now see if there is already a file with this *name* created
00582     {
00583       GCriticalSectionLock lock(&inc_files_lock);
00584       GPosition pos;
00585       for(pos=inc_files_list;pos;++pos)
00586       {
00587         if (inc_files_list[pos]->url.fname()==incl_url.fname())
00588            break;
00589       }
00590       if (pos)
00591         return inc_files_list[pos];
00592     }
00593     
00594     // No. We have to request a new file
00595     GP<DjVuFile> file;
00596     G_TRY
00597     {
00598       file=pcaster->id_to_file(this, incl_str);
00599     }
00600     G_CATCH(ex)
00601     {
00602       unlink_file(incl_str);
00603       // In order to keep compatibility with the previous
00604       // release of the DjVu plugin, we will not interrupt
00605       // decoding here. We will just report the error.
00606       // NOTE, that it's now the responsibility of the
00607       // decoder to resolve all chunk dependencies, and
00608       // abort decoding if necessary.
00609       
00610       // G_EXTHROW(ex); /* commented out */
00611       
00612       get_portcaster()->notify_error(this,ex.get_cause());
00613       return 0;
00614     }
00615     G_ENDCATCH;
00616     if (!file)
00617     {
00618       G_THROW( ERR_MSG("DjVuFile.no_create") "\t"+incl_str);
00619     }
00620     if (recover_errors!=ABORT)
00621       file->set_recover_errors(recover_errors);
00622     if (verbose_eof)
00623       file->set_verbose_eof(verbose_eof);
00624     pcaster->add_route(file, this);
00625     
00626     // We may have been stopped. Make sure the child will be stopped too.
00627     if (flags & STOPPED)
00628       file->stop(false);
00629     if (flags & BLOCKED_STOPPED)
00630       file->stop(true);
00631     
00632     // Lock the list again and check if the file has already been
00633     // added by someone else
00634     {
00635       GCriticalSectionLock lock(&inc_files_lock);
00636       GPosition pos;
00637       for(pos=inc_files_list;pos;++pos)
00638       {
00639         if (inc_files_list[pos]->url.fname()==incl_url.fname())
00640           break;
00641       }
00642       if (pos)
00643       {
00644         file=inc_files_list[pos];
00645       } else if (file_num<0 || !(pos=inc_files_list.nth(file_num)))
00646       {
00647         inc_files_list.append(file);
00648       } else 
00649       {
00650         inc_files_list.insert_before(pos, file);
00651       }
00652     }
00653     return file;
00654   }
00655   return 0;
00656 }
00657 
00658 
00659 void
00660 DjVuFile::report_error(const GException &ex,bool throw_errors)
00661 {
00662   data_pool->clear_stream();
00663   if((!verbose_eof)|| (ex.cmp_cause(ByteStream::EndOfFile)))
00664   {
00665     if(throw_errors)
00666     {
00667       G_EXTHROW(ex);
00668     }else
00669     {
00670       get_portcaster()->notify_error(this,ex.get_cause());
00671     }
00672   }else
00673   {
00674     GURL url=get_url();
00675     GUTF8String url_str=url.get_string();
00676 //    if (url.is_local_file_url())
00677 //      url_str=url.filename();
00678     
00679     GUTF8String msg = GUTF8String( ERR_MSG("DjVuFile.EOF") "\t") + url;
00680     if(throw_errors)
00681     {
00682       G_EXTHROW(ex, msg);
00683     }else
00684     {
00685       get_portcaster()->notify_error(this,msg);
00686     }
00687   }
00688 }
00689 
00690 void
00691 DjVuFile::process_incl_chunks(void)
00692 // This function may block for data
00693 // NOTE: It may be called again when INCL_FILES_CREATED is set.
00694 // It happens in insert_file() when it has modified the data
00695 // and wants to create the actual file
00696 {
00697   DEBUG_MSG("DjVuFile::process_incl_chunks(void)\n");
00698   DEBUG_MAKE_INDENT(3);
00699   check();
00700   
00701   int incl_cnt=0;
00702   
00703   const GP<ByteStream> str(data_pool->get_stream());
00704   GUTF8String chkid;
00705   const GP<IFFByteStream> giff(IFFByteStream::create(str));
00706   IFFByteStream &iff=*giff;
00707   if (iff.get_chunk(chkid))
00708   {
00709     int chunks=0;
00710     int last_chunk=0;
00711     G_TRY
00712     {
00713       int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
00714       int chksize;
00715       for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
00716       {
00717         chunks++;
00718         if (chkid=="INCL")
00719         {
00720           G_TRY
00721           {
00722             process_incl_chunk(*iff.get_bytestream(), incl_cnt++);
00723           }
00724           G_CATCH(ex);
00725           {
00726             report_error(ex,(recover_errors <= SKIP_PAGES));
00727           }
00728           G_ENDCATCH;
00729         }else if(chkid=="FAKE")
00730         {
00731           set_needs_compression(true);
00732           set_can_compress(true);
00733         }else if(chkid=="BGjp")
00734         {
00735           set_can_compress(true);
00736         }else if(chkid=="Smmr")
00737         {
00738           set_can_compress(true);
00739         }
00740         iff.seek_close_chunk();
00741       }
00742       if (chunks_number < 0) chunks_number=last_chunk;
00743     }
00744     G_CATCH(ex)
00745     {   
00746       if (chunks_number < 0)
00747         chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
00748       report_error(ex,(recover_errors <= SKIP_PAGES));
00749     }
00750     G_ENDCATCH;
00751   }
00752   flags|=INCL_FILES_CREATED;
00753   data_pool->clear_stream();
00754 }
00755 
00756 GP<JB2Dict>
00757 DjVuFile::static_get_fgjd(void *arg)
00758 {
00759   DjVuFile *file = (DjVuFile*)arg;
00760   return file->get_fgjd(1);
00761 }
00762 
00763 GP<JB2Dict>
00764 DjVuFile::get_fgjd(int block)
00765 {
00766   check();
00767   
00768   // Simplest case
00769   if (fgjd)
00770     return fgjd;
00771   // Check wether included files
00772   chunk_mon.enter();
00773   G_TRY {
00774     for(;;)
00775     {
00776       int active = 0;
00777       GPList<DjVuFile> incs = get_included_files();
00778       for (GPosition pos=incs.firstpos(); pos; ++pos)
00779       {
00780         GP<DjVuFile> file = incs[pos];
00781         if (file->is_decoding())
00782           active = 1;
00783         GP<JB2Dict> fgjd = file->get_fgjd();
00784         if (fgjd)
00785         {
00786           chunk_mon.leave();
00787           return fgjd;
00788         }
00789       }
00790       // Exit if non-blocking mode
00791       if (! block)
00792         break;
00793       // Exit if there is no decoding activity
00794       if (! active)
00795         break;
00796       // Wait until a new chunk gets decoded
00797       wait_for_chunk();
00798     }
00799   } G_CATCH_ALL {
00800     chunk_mon.leave();
00801     G_RETHROW;
00802   } G_ENDCATCH;
00803   chunk_mon.leave();
00804   if (is_decode_stopped()) G_THROW( DataPool::Stop );
00805   return 0;
00806 }
00807 
00808 int
00809 DjVuFile::get_dpi(int w, int h)
00810 {
00811   int dpi=0, red=1;
00812   if (info)
00813   {
00814     for(red=1; red<=12; red++)
00815       if ((info->width+red-1)/red==w)
00816         if ((info->height+red-1)/red==h)
00817           break;
00818     if (red>12)
00819       G_THROW( ERR_MSG("DjVuFile.corrupt_BG44") );
00820     dpi=info->dpi;
00821   }
00822   return (dpi ? dpi : 300)/red;
00823 }
00824 
00825 static inline bool
00826 is_info(const GUTF8String &chkid)
00827 {
00828   return (chkid=="INFO");
00829 }
00830 
00831 static inline bool
00832 is_annotation(const GUTF8String &chkid)
00833 {
00834   return (chkid=="ANTa" ||
00835     chkid=="ANTz" ||
00836     chkid=="FORM:ANNO" ); 
00837 }
00838 
00839 static inline bool
00840 is_text(const GUTF8String &chkid)
00841 {
00842   return (chkid=="TXTa" || chkid=="TXTz");
00843 }
00844 
00845 static inline bool
00846 is_meta(const GUTF8String &chkid)
00847 {
00848   return (chkid=="METa" || chkid=="METz");
00849 }
00850 
00851 
00852 GUTF8String
00853 DjVuFile::decode_chunk( const GUTF8String &id, const GP<ByteStream> &gbs,
00854   bool djvi, bool djvu, bool iw44)
00855 {
00856   DEBUG_MSG("DjVuFile::decode_chunk()\n");
00857   ByteStream &bs=*gbs;
00858   check();
00859   
00860   // If this object is referenced by only one GP<> pointer, this
00861   // pointer should be the "life_saver" created by the decoding thread.
00862   // If it is the only GP<> pointer, then nobody is interested in the
00863   // results of the decoding and we can abort now with #DataPool::Stop#
00864   if (get_count()==1)
00865     G_THROW( DataPool::Stop );
00866   
00867   GUTF8String desc = ERR_MSG("DjVuFile.unrecog_chunk");
00868   GUTF8String chkid = id;
00869   DEBUG_MSG("DjVuFile::decode_chunk() : decoding " << id << "\n");
00870   
00871   // INFO  (information chunk for djvu page)
00872   if (is_info(chkid) && (djvu || djvi))
00873   {
00874     if (info)
00875       G_THROW( ERR_MSG("DjVuFile.corrupt_dupl") );
00876     if (djvi)
00877       G_THROW( ERR_MSG("DjVuFile.corrupt_INFO") );
00878     // DjVuInfo::decode no longer throws version exceptions
00879     GP<DjVuInfo> xinfo=DjVuInfo::create();
00880     xinfo->decode(bs);
00881     info = xinfo;
00882     desc.format( ERR_MSG("DjVuFile.page_info") );
00883     // Consistency checks (previously in DjVuInfo::decode)
00884     if (info->width<0 || info->height<0)
00885       G_THROW( ERR_MSG("DjVuFile.corrupt_zero") );
00886     if (info->version >= DJVUVERSION_TOO_NEW)
00887       G_THROW( ERR_MSG("DjVuFile.new_version") "\t" STRINGIFY(DJVUVERSION_TOO_NEW) );
00888     if(info->compressable)
00889       set_can_compress(true);
00890   }
00891   
00892   // INCL (inclusion chunk)
00893   else if (chkid == "INCL" && (djvi || djvu || iw44))
00894   {
00895     GP<DjVuFile> file=process_incl_chunk(bs);
00896     if (file)
00897     {
00898       int decode_was_already_started = 1;
00899       {
00900         GMonitorLock lock(&file->flags);
00901           // Start decoding
00902         if(file->resume_decode())
00903         {
00904           decode_was_already_started = 0;
00905         }
00906       }
00907       // Send file notifications if previously started
00908       if (decode_was_already_started)
00909       {
00910         // May send duplicate notifications...
00911         if (file->is_decode_ok())
00912           get_portcaster()->notify_file_flags_changed(file, DECODE_OK, 0);
00913         else if (file->is_decode_failed())
00914           get_portcaster()->notify_file_flags_changed(file, DECODE_FAILED, 0);
00915       }
00916       desc.format( ERR_MSG("DjVuFile.indir_chunk1") "\t" + file->get_url().fname() );
00917     } else
00918       desc.format( ERR_MSG("DjVuFile.indir_chunk2") );
00919   }
00920   
00921   // Djbz (JB2 Dictionary)
00922   else if (chkid == "Djbz" && (djvu || djvi))
00923   {
00924     if (this->fgjd)
00925       G_THROW( ERR_MSG("DjVuFile.dupl_Dxxx") );
00926     if (this->fgjd)
00927       G_THROW( ERR_MSG("DjVuFile.Dxxx_after_Sxxx") );
00928     GP<JB2Dict> fgjd = JB2Dict::create();
00929     fgjd->decode(gbs);
00930     this->fgjd = fgjd;
00931     desc.format( ERR_MSG("DjVuFile.shape_dict") "\t%d", fgjd->get_shape_count() );
00932   } 
00933   
00934   // Sjbz (JB2 encoded mask)
00935   else if (chkid=="Sjbz" && (djvu || djvi))
00936   {
00937     if (this->fgjb)
00938       G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
00939     GP<JB2Image> fgjb=JB2Image::create();
00940     // ---- begin hack
00941     if (info && info->version <=18)
00942       fgjb->reproduce_old_bug = true;
00943     // ---- end hack
00944     fgjb->decode(gbs, static_get_fgjd, (void*)this);
00945     this->fgjb = fgjb;
00946     desc.format( ERR_MSG("DjVuFile.fg_mask") "\t%d\t%d\t%d",
00947       fgjb->get_width(), fgjb->get_height(),
00948       get_dpi(fgjb->get_width(), fgjb->get_height()));
00949   }
00950   
00951   // Smmr (MMR-G4 encoded mask)
00952   else if (chkid=="Smmr" && (djvu || djvi))
00953   {
00954     if (this->fgjb)
00955       G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
00956     set_can_compress(true);
00957     this->fgjb = MMRDecoder::decode(gbs);
00958     desc.format( ERR_MSG("DjVuFile.G4_mask") "\t%d\t%d\t%d",
00959       fgjb->get_width(), fgjb->get_height(),
00960       get_dpi(fgjb->get_width(), fgjb->get_height()));
00961   }
00962   
00963   // BG44 (background wavelets)
00964   else if (chkid == "BG44" && (djvu || djvi))
00965   {
00966     if (!bg44)
00967     {
00968       if (bgpm)
00969         G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
00970       // First chunk
00971       GP<IW44Image> bg44=IW44Image::create_decode(IW44Image::COLOR);
00972       bg44->decode_chunk(gbs);
00973       this->bg44 = bg44;
00974       desc.format( ERR_MSG("DjVuFile.IW44_bg1") "\t%d\t%d\t%d",
00975               bg44->get_width(), bg44->get_height(),
00976           get_dpi(bg44->get_width(), bg44->get_height()));
00977     } 
00978     else
00979     {
00980       // Refinement chunks
00981       GP<IW44Image> bg44 = this->bg44;
00982       bg44->decode_chunk(gbs);
00983       desc.format( ERR_MSG("DjVuFile.IW44_bg2") "\t%d\t%d",
00984               bg44->get_serial(), get_dpi(bg44->get_width(), bg44->get_height()));
00985     }
00986   }
00987   
00988   // FG44 (foreground wavelets)
00989   else if (chkid == "FG44" && (djvu || djvu))
00990   {
00991     if (fgpm || fgbc)
00992       G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
00993     GP<IW44Image> gfg44=IW44Image::create_decode(IW44Image::COLOR);
00994     IW44Image &fg44=*gfg44;
00995     fg44.decode_chunk(gbs);
00996     fgpm=fg44.get_pixmap();
00997     desc.format( ERR_MSG("DjVuFile.IW44_fg") "\t%d\t%d\t%d",
00998       fg44.get_width(), fg44.get_height(),
00999       get_dpi(fg44.get_width(), fg44.get_height()));
01000   } 
01001   
01002   // LINK (background LINK)
01003   else if (chkid == "LINK" && (djvu || djvi))
01004   {
01005     if (bg44 || bgpm)
01006       G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01007     if(djvu_decode_codec)
01008     {
01009       set_modified(true);
01010       set_can_compress(true);
01011       set_needs_compression(true);
01012       this->bgpm = djvu_decode_codec(bs);
01013       desc.format( ERR_MSG("DjVuFile.color_import1") "\t%d\t%d\t%d",
01014         bgpm->columns(), bgpm->rows(),
01015         get_dpi(bgpm->columns(), bgpm->rows()));
01016     }else
01017     {
01018       desc.format( ERR_MSG("DjVuFile.color_import2") );
01019     }
01020   } 
01021   
01022   // BGjp (background JPEG)
01023   else if (chkid == "BGjp" && (djvu || djvi))
01024   {
01025     if (bg44 || bgpm)
01026       G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01027     set_can_compress(true);
01028 #ifdef NEED_JPEG_DECODER
01029     this->bgpm = JPEGDecoder::decode(bs);
01030     desc.format( ERR_MSG("DjVuFile.JPEG_bg1") "\t%d\t%d\t%d",
01031       bgpm->columns(), bgpm->rows(),
01032       get_dpi(bgpm->columns(), bgpm->rows()));
01033 #else
01034     desc.format( ERR_MSG("DjVuFile.JPEG_bg2") );
01035 #endif
01036   } 
01037   
01038   // FGjp (foreground JPEG)
01039   else if (chkid == "FGjp" && (djvu || djvi))
01040   {
01041     if (fgpm || fgbc)
01042       G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01043 #ifdef NEED_JPEG_DECODER
01044     this->fgpm = JPEGDecoder::decode(bs);
01045     desc.format( ERR_MSG("DjVuFile.JPEG_fg1") "\t%d\t%d\t%d",
01046       fgpm->columns(), fgpm->rows(),
01047       get_dpi(fgpm->columns(), fgpm->rows()));
01048 #else
01049     desc.format( ERR_MSG("DjVuFile.JPEG_fg2") );
01050 #endif
01051   } 
01052   
01053   // BG2k (background JPEG-2000) Note: JPEG2K bitstream not finalized.
01054   else if (chkid == "BG2k" && (djvu || djvi))
01055   {
01056     if (bg44)
01057       G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01058     desc.format( ERR_MSG("DjVuFile.JPEG2K_bg") );
01059   } 
01060   
01061   // FG2k (foreground JPEG-2000) Note: JPEG2K bitstream not finalized.
01062   else if (chkid == "FG2k" && (djvu || djvi))
01063   {
01064     if (fgpm || fgbc)
01065       G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01066     desc.format( ERR_MSG("DjVuFile.JPEG2K_fg") );
01067   } 
01068   
01069   // FGbz (foreground color vector)
01070   else if (chkid == "FGbz" && (djvu || djvi))
01071   {
01072     if (fgpm || fgbc)
01073       G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01074     GP<DjVuPalette> fgbc = DjVuPalette::create();
01075     fgbc->decode(gbs);
01076     this->fgbc = fgbc;
01077     desc.format( ERR_MSG("DjVuFile.JB2_fg") "\t%d\t%d",
01078       fgbc->size(), fgbc->colordata.size());
01079   }
01080   
01081   // BM44/PM44 (IW44 data)
01082   else if ((chkid == "PM44" || chkid=="BM44") && iw44)
01083   {
01084     if (!bg44)
01085     {
01086       // First chunk
01087       GP<IW44Image> bg44 = IW44Image::create_decode(IW44Image::COLOR);
01088       bg44->decode_chunk(gbs);
01089       GP<DjVuInfo> info = DjVuInfo::create();
01090       info->width = bg44->get_width();
01091       info->height = bg44->get_height();
01092       info->dpi = 100;
01093       this->bg44 = bg44;
01094       this->info = info;
01095       desc.format( ERR_MSG("DjVuFile.IW44_data1") "\t%d\t%d\t%d",
01096                    bg44->get_width(), bg44->get_height(),
01097                    get_dpi(bg44->get_width(), bg44->get_height()));
01098     } 
01099     else
01100     {
01101       // Refinement chunks
01102       GP<IW44Image> bg44 = this->bg44;
01103       bg44->decode_chunk(gbs);
01104       desc.format( ERR_MSG("DjVuFile.IW44_data2") "\t%d\t%d",
01105                    bg44->get_serial(),
01106                    get_dpi(bg44->get_width(), bg44->get_height()));
01107     }
01108   }
01109   
01110   // NDIR (obsolete navigation chunk)
01111   else if (chkid == "NDIR")
01112   {
01113     GP<DjVuNavDir> dir=DjVuNavDir::create(url);
01114     dir->decode(bs);
01115     this->dir=dir;
01116     desc.format( ERR_MSG("DjVuFile.nav_dir") );
01117   }
01118   
01119   // FORM:ANNO (obsolete) (must be before other annotations)
01120   else if (chkid == "FORM:ANNO") 
01121     {
01122       const GP<ByteStream> gachunk(ByteStream::create());
01123       ByteStream &achunk=*gachunk;
01124       achunk.copy(bs);
01125       achunk.seek(0);
01126       GCriticalSectionLock lock(&anno_lock);
01127       if (! anno)
01128       {
01129         anno=ByteStream::create();
01130       }
01131       anno->seek(0,SEEK_END);
01132       if (anno->tell())
01133       {
01134         anno->write((void*)"", 1);
01135       }
01136       // Copy data
01137       anno->copy(achunk);
01138       desc.format( ERR_MSG("DjVuFile.anno1") );
01139     }
01140   
01141   // ANTa/ANTx/TXTa/TXTz annotations
01142   else if (is_annotation(chkid))  // but not FORM:ANNO
01143     {
01144       const GP<ByteStream> gachunk(ByteStream::create());
01145       ByteStream &achunk=*gachunk;
01146       achunk.copy(bs);
01147       achunk.seek(0);
01148       GCriticalSectionLock lock(&anno_lock);
01149       if (! anno)
01150       {
01151         anno = ByteStream::create();
01152       }
01153       anno->seek(0,SEEK_END);
01154       if (anno->tell() & 1)
01155       {
01156         anno->write((const void*)"", 1);
01157       }
01158       // Recreate chunk header
01159       const GP<IFFByteStream> giffout(IFFByteStream::create(anno));
01160       IFFByteStream &iffout=*giffout;
01161       iffout.put_chunk(id);
01162       iffout.copy(achunk);
01163       iffout.close_chunk();
01164       desc.format( ERR_MSG("DjVuFile.anno2") );
01165     }
01166   else if (is_text(chkid))
01167     {
01168       const GP<ByteStream> gachunk(ByteStream::create());
01169       ByteStream &achunk=*gachunk;
01170       achunk.copy(bs);
01171       achunk.seek(0);
01172       GCriticalSectionLock lock(&text_lock);
01173       if (! text)
01174       {
01175         text = ByteStream::create();
01176       }
01177       text->seek(0,SEEK_END);
01178       if (text->tell())
01179       {
01180         text->write((const void*)"", 1);
01181       }
01182       // Recreate chunk header
01183       const GP<IFFByteStream> giffout(IFFByteStream::create(text));
01184       IFFByteStream &iffout=*giffout;
01185       iffout.put_chunk(id);
01186       iffout.copy(achunk);
01187       iffout.close_chunk();
01188       desc.format( ERR_MSG("DjVuFile.text") );
01189     }
01190   else if (is_meta(chkid))
01191     {
01192       const GP<ByteStream> gachunk(ByteStream::create());
01193       ByteStream &achunk=*gachunk;
01194       achunk.copy(bs);
01195       achunk.seek(0);
01196       GCriticalSectionLock lock(&text_lock);
01197       if (! meta)
01198       {
01199         meta = ByteStream::create();
01200       }
01201       meta->seek(0,SEEK_END);
01202       if (meta->tell())
01203       {
01204         meta->write((const void*)"", 1);
01205       }
01206       // Recreate chunk header
01207       const GP<IFFByteStream> giffout(IFFByteStream::create(meta));
01208       IFFByteStream &iffout=*giffout;
01209       iffout.put_chunk(id);
01210       iffout.copy(achunk);
01211       iffout.close_chunk();
01212 //      desc.format( ERR_MSG("DjVuFile.text") );
01213     }
01214 
01215   // Return description
01216   return desc;
01217 }
01218 
01219 void
01220 DjVuFile::set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs))
01221 {
01222   djvu_decode_codec=codec;
01223 }
01224 
01225 void
01226 DjVuFile::decode(const GP<ByteStream> &gbs)
01227 {
01228   check();
01229   DEBUG_MSG("DjVuFile::decode(), url='" << url << "'\n");
01230   DEBUG_MAKE_INDENT(3);
01231   DjVuPortcaster * pcaster=get_portcaster();
01232   
01233   // Get form chunk
01234   GUTF8String chkid;
01235   const GP<IFFByteStream> giff(IFFByteStream::create(gbs));
01236   IFFByteStream &iff=*giff;
01237   if (!iff.get_chunk(chkid)) 
01238     REPORT_EOF(true)
01239     
01240     // Check file format
01241   bool djvi = (chkid=="FORM:DJVI")?true:false;
01242   bool djvu = (chkid=="FORM:DJVU")?true:false;
01243   bool iw44 = ((chkid=="FORM:PM44") || (chkid=="FORM:BM44"));
01244   if (djvi || djvu)
01245     mimetype = "image/x.djvu";
01246   else if (iw44)
01247     mimetype = "image/x-iw44";
01248   else
01249     G_THROW( ERR_MSG("DjVuFile.unexp_image") );
01250   
01251   // Process chunks
01252   int size_so_far=iff.tell();
01253   int chunks=0;
01254   int last_chunk=0;
01255   G_TRY
01256   {
01257     int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
01258     int chksize;
01259     for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
01260     {
01261       chunks++;
01262 
01263       // Decode and get chunk description
01264       GUTF8String str = decode_chunk(chkid, iff.get_bytestream(), djvi, djvu, iw44);
01265       // Add parameters to the chunk description to give the size and chunk id
01266       GUTF8String desc;
01267       desc.format("\t%5.1f\t%s", chksize/1024.0, (const char*)chkid);
01268       // Append the whole thing to the growing file description
01269       description = description + str + desc + "\n";
01270 
01271       pcaster->notify_chunk_done(this, chkid);
01272       // Close chunk
01273       iff.seek_close_chunk();
01274       // Record file size
01275       size_so_far=iff.tell();
01276     }
01277     if (chunks_number < 0) chunks_number=last_chunk;
01278   }
01279   G_CATCH(ex)
01280   {
01281     if(!ex.cmp_cause(ByteStream::EndOfFile))
01282     {
01283       if (chunks_number < 0)
01284         chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
01285       report_error(ex,(recover_errors <= SKIP_PAGES));
01286     }else
01287     {
01288       report_error(ex,true);
01289     }
01290   }
01291   G_ENDCATCH;
01292   
01293   // Record file size
01294   file_size=size_so_far;
01295   // Close form chunk
01296   iff.close_chunk();
01297   // Close BG44 codec
01298   if (bg44) 
01299     bg44->close_codec();
01300   
01301   // Complete description
01302   if (djvu && !info)
01303     G_THROW( ERR_MSG("DjVuFile.corrupt_missing_info") );
01304   if (iw44 && !info)
01305     G_THROW( ERR_MSG("DjVuFile.corrupt_missing_IW44") );
01306   if (info)
01307   {
01308     GUTF8String desc;
01309     if (djvu || djvi)
01310       desc.format( ERR_MSG("DjVuFile.djvu_header") "\t%d\t%d\t%d\t%d", 
01311         info->width, info->height,
01312         info->dpi, info->version);
01313     else if (iw44)
01314       desc.format( ERR_MSG("DjVuFile.IW44_header") "\t%d\t%d\t%d", 
01315         info->width, info->height, info->dpi);
01316     description=desc + "\n" + description;
01317     int rawsize=info->width*info->height*3;
01318     desc.format( ERR_MSG("DjVuFile.ratio") "\t%0.1f\t%0.1f",
01319       (double)rawsize/file_size, file_size/1024.0 );
01320     description=description+desc;
01321   }
01322 }
01323 
01324 void
01325 DjVuFile::start_decode(void)
01326 {
01327   check();
01328   DEBUG_MSG("DjVuFile::start_decode(), url='" << url << "'\n");
01329   DEBUG_MAKE_INDENT(3);
01330   
01331   GThread * thread_to_delete=0;
01332   flags.enter();
01333   G_TRY {
01334     if (!(flags & DONT_START_DECODE) && !is_decoding())
01335     {
01336       if (flags & DECODE_STOPPED) reset();
01337       flags&=~(DECODE_OK | DECODE_STOPPED | DECODE_FAILED);
01338       flags|=DECODING;
01339       
01340       // Don't delete the thread while you're owning the flags lock
01341       // Beware of deadlock!
01342       thread_to_delete=decode_thread; decode_thread=0;
01343       
01344       // We want to create it right here to be able to stop the
01345       // decoding thread even before its function is called (it starts)
01346       decode_data_pool=DataPool::create(data_pool);
01347       decode_life_saver=this;
01348       
01349       decode_thread=new GThread();
01350       decode_thread->create(static_decode_func, this);
01351     }
01352   }
01353   G_CATCH_ALL
01354   {
01355     flags&=~DECODING;
01356     flags|=DECODE_FAILED;
01357     flags.leave();
01358     get_portcaster()->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
01359     delete thread_to_delete;
01360     G_RETHROW;
01361   }
01362   G_ENDCATCH;
01363   flags.leave();
01364   delete thread_to_delete;
01365 }
01366 
01367 bool
01368 DjVuFile::resume_decode(const bool sync)
01369 {
01370   bool retval=false;
01371   {
01372     GMonitorLock lock(&flags);
01373     if( !is_decoding() && !is_decode_ok() && !is_decode_failed() )
01374     {
01375       start_decode();
01376       retval=true;
01377     }
01378   }
01379   if(sync)
01380   {
01381     wait_for_finish();
01382   }
01383   return retval;
01384 }
01385 
01386 void
01387 DjVuFile::stop_decode(bool sync)
01388 {
01389   check();
01390   
01391   DEBUG_MSG("DjVuFile::stop_decode(), url='" << url <<
01392     "', sync=" << (int) sync << "\n");
01393   DEBUG_MAKE_INDENT(3);
01394   
01395   G_TRY
01396   {
01397     flags|=DONT_START_DECODE;
01398     
01399     // Don't stop SYNCHRONOUSLY from the thread where the decoding is going!!!
01400     {
01401       // First - ask every included child to stop in async mode
01402       GCriticalSectionLock lock(&inc_files_lock);
01403       for(GPosition pos=inc_files_list;pos;++pos)
01404         inc_files_list[pos]->stop_decode(0);
01405       
01406 //      if (decode_data_pool) decode_data_pool->stop();
01407     }
01408     
01409     if (sync)
01410     {
01411       while(1)
01412       {
01413         GP<DjVuFile> file;
01414         {
01415           GCriticalSectionLock lock(&inc_files_lock);
01416           for(GPosition pos=inc_files_list;pos;++pos)
01417           {
01418             GP<DjVuFile> & f=inc_files_list[pos];
01419             if (f->is_decoding())
01420             {
01421               file=f; break;
01422             }
01423           }
01424         }
01425         if (!file) break;
01426         
01427         file->stop_decode(1);
01428       }
01429       
01430       wait_for_finish(1);   // Wait for self termination
01431       
01432       // Don't delete the thread here. Until GPBase::preserve() is
01433       // reimplemented somehow at the GThread level.
01434       // delete decode_thread; decode_thread=0;
01435     }
01436     flags&=~(DONT_START_DECODE);
01437   } G_CATCH_ALL {
01438     flags&=~(DONT_START_DECODE);
01439     G_RETHROW;
01440   } G_ENDCATCH;
01441 }
01442 
01443 void
01444 DjVuFile::stop(bool only_blocked)
01445 // This is a one-way function. There is no way to undo the stop()
01446 // command.
01447 {
01448   DEBUG_MSG("DjVuFile::stop(): Stopping everything\n");
01449   DEBUG_MAKE_INDENT(3);
01450   
01451   flags|=only_blocked ? BLOCKED_STOPPED : STOPPED;
01452   if (data_pool) data_pool->stop(only_blocked);
01453   GCriticalSectionLock lock(&inc_files_lock);
01454   for(GPosition pos=inc_files_list;pos;++pos)
01455     inc_files_list[pos]->stop(only_blocked);
01456 }
01457 
01458 GP<DjVuNavDir>
01459 DjVuFile::find_ndir(GMap<GURL, void *> & map)
01460 {
01461   check();
01462   
01463   DEBUG_MSG("DjVuFile::find_ndir(): looking for NDIR in '" << url << "'\n");
01464   DEBUG_MAKE_INDENT(3);
01465   
01466   if (dir) return dir;
01467   
01468   if (!map.contains(url))
01469   {
01470     map[url]=0;
01471     
01472     GPList<DjVuFile> list=get_included_files(false);
01473     for(GPosition pos=list;pos;++pos)
01474     {
01475       GP<DjVuNavDir> d=list[pos]->find_ndir(map);
01476       if (d) return d;
01477     }
01478   }
01479   return 0;
01480 }
01481 
01482 GP<DjVuNavDir>
01483 DjVuFile::find_ndir(void)
01484 {
01485   GMap<GURL, void *> map;
01486   return find_ndir(map);
01487 }
01488 
01489 GP<DjVuNavDir>
01490 DjVuFile::decode_ndir(GMap<GURL, void *> & map)
01491 {
01492   check();
01493   
01494   DEBUG_MSG("DjVuFile::decode_ndir(): decoding for NDIR in '" << url << "'\n");
01495   DEBUG_MAKE_INDENT(3);
01496   
01497   if (dir) return dir;
01498   
01499   if (!map.contains(url))
01500   {
01501     map[url]=0;
01502     
01503     const GP<ByteStream> str(data_pool->get_stream());
01504     
01505     GUTF8String chkid;
01506     const GP<IFFByteStream> giff(IFFByteStream::create(str));
01507     IFFByteStream &iff=*giff;
01508     if (!iff.get_chunk(chkid)) 
01509       REPORT_EOF(true)
01510       
01511     int chunks=0;
01512     int last_chunk=0;
01513     G_TRY
01514     {
01515       int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
01516       int chksize;
01517       for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
01518       {
01519         chunks++;
01520         if (chkid=="NDIR")
01521         {
01522           GP<DjVuNavDir> d=DjVuNavDir::create(url);
01523           d->decode(*iff.get_bytestream());
01524           dir=d;
01525           break;
01526         }
01527         iff.seek_close_chunk();
01528       }
01529       if ((!dir)&&(chunks_number < 0)) chunks_number=last_chunk;
01530     }
01531     G_CATCH(ex)
01532     {
01533        if(!ex.cmp_cause(ByteStream::EndOfFile))
01534        {
01535           if (chunks_number < 0)
01536              chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
01537           report_error(ex,(recover_errors<=SKIP_PAGES));
01538        }else
01539        {
01540           report_error(ex,true);
01541        }
01542     }
01543     G_ENDCATCH;
01544     
01545     data_pool->clear_stream();
01546     if (dir) return dir;
01547     
01548     GPList<DjVuFile> list=get_included_files(false);
01549     for(GPosition pos=list;pos;++pos)
01550     {
01551       GP<DjVuNavDir> d=list[pos]->decode_ndir(map);
01552       if (d) return d;
01553     }
01554     data_pool->clear_stream();
01555   }
01556   return 0;
01557 }
01558 
01559 GP<DjVuNavDir>
01560 DjVuFile::decode_ndir(void)
01561 {
01562   GMap<GURL, void *> map;
01563   return decode_ndir(map);
01564 }
01565 
01566 void
01567 DjVuFile::get_merged_anno(const GP<DjVuFile> & file,
01568   const GP<ByteStream> &gstr_out, const GList<GURL> & ignore_list,
01569   int level, int & max_level, GMap<GURL, void *> & map)
01570 {
01571   DEBUG_MSG("DjVuFile::get_merged_anno()\n");
01572   GURL url=file->get_url();
01573   if (!map.contains(url))
01574   {
01575     ByteStream &str_out=*gstr_out;
01576     map[url]=0;
01577     
01578     // Do the included files first (To make sure that they have
01579     // less precedence)
01580     // Depending on if we have all data present, we will
01581     // either create all included files or will use only
01582     // those that have already been created
01583     GPList<DjVuFile> list=file->get_included_files(!file->is_data_present());
01584     for(GPosition pos=list;pos;++pos)
01585       get_merged_anno(list[pos], gstr_out, ignore_list, level+1, max_level, map);
01586     
01587     // Now process the DjVuFile's own annotations
01588     if (!ignore_list.contains(file->get_url()))
01589     {
01590       if (!file->is_data_present() ||
01591         file->is_modified() && file->anno)
01592       {
01593            // Process the decoded (?) anno
01594         GCriticalSectionLock lock(&file->anno_lock);
01595         if (file->anno && file->anno->size())
01596         {
01597           if (str_out.tell())
01598           {
01599             str_out.write((void *) "", 1);
01600           }
01601           file->anno->seek(0);
01602           str_out.copy(*file->anno);
01603         }
01604       } else if (file->is_data_present())
01605       {
01606            // Copy all annotations chunks, but do NOT modify
01607            // this->anno (to avoid correlation with DjVuFile::decode())
01608         const GP<ByteStream> str(file->data_pool->get_stream());
01609         const GP<IFFByteStream> giff(IFFByteStream::create(str));
01610         IFFByteStream &iff=*giff;
01611         GUTF8String chkid;
01612         if (iff.get_chunk(chkid))
01613           while(iff.get_chunk(chkid))
01614           {
01615             if (chkid=="FORM:ANNO")
01616             {
01617               if (max_level<level)
01618                 max_level=level;
01619               if (str_out.tell())
01620               {
01621                 str_out.write((void *) "", 1);
01622               }
01623               str_out.copy(*iff.get_bytestream());
01624             } 
01625             else if (is_annotation(chkid)) // but not FORM:ANNO
01626             {
01627               if (max_level<level)
01628                 max_level=level;
01629               if (str_out.tell()&&chkid != "ANTz")
01630               {
01631                 str_out.write((void *) "", 1);
01632               }
01633               const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01634               IFFByteStream &iff_out=*giff_out;
01635               iff_out.put_chunk(chkid);
01636               iff_out.copy(*iff.get_bytestream());
01637               iff_out.close_chunk();
01638             }
01639             iff.close_chunk();
01640           }
01641         file->data_pool->clear_stream();
01642       }
01643     }
01644   }
01645 }
01646 
01647 GP<ByteStream>
01648 DjVuFile::get_merged_anno(const GList<GURL> & ignore_list,
01649                           int * max_level_ptr)
01650                           // Will do the same thing as get_merged_anno(int *), but will
01651                           // ignore DjVuFiles with URLs from the ignore_list
01652 {
01653   DEBUG_MSG("DjVuFile::get_merged_anno()\n");
01654   GP<ByteStream> gstr(ByteStream::create());
01655   GMap<GURL, void *> map;
01656   int max_level=0;
01657   get_merged_anno(this, gstr, ignore_list, 0, max_level, map);
01658   if (max_level_ptr)
01659     *max_level_ptr=max_level;
01660   ByteStream &str=*gstr;
01661   if (!str.tell()) 
01662   {
01663     gstr=0;
01664   }else
01665   {
01666     str.seek(0);
01667   }
01668   return gstr;
01669 }
01670 
01671 GP<ByteStream>
01672 DjVuFile::get_merged_anno(int * max_level_ptr)
01673 // Will go down the DjVuFile's hierarchy and decode all DjVuAnno even
01674 // when the DjVuFile is not fully decoded yet. To avoid correlations
01675 // with DjVuFile::decode(), we do not modify DjVuFile::anno data.
01676 //
01677 // Files deeper in the hierarchy have less influence on the
01678 // results. It means, for example, that the if annotations are
01679 // specified in the top level page file and in a shared file,
01680 // the top level page file settings will take precedence.
01681 //
01682 // NOTE! This function guarantees correct results only if the
01683 // DjVuFile has all data
01684 {
01685   GList<GURL> ignore_list;
01686   return get_merged_anno(ignore_list, max_level_ptr);
01687 }
01688 
01689 
01690 // [LB->BCR] The following six functions get_anno, get_text, get_meta 
01691 // contain the same code in triplicate!!!
01692 
01693 void
01694 DjVuFile::get_anno(
01695   const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01696 {
01697   DEBUG_MSG("DjVuFile::get_anno()\n");
01698   ByteStream &str_out=*gstr_out;
01699   if (!file->is_data_present() ||
01700     file->is_modified() && file->anno)
01701   {
01702     // Process the decoded (?) anno
01703     GCriticalSectionLock lock(&file->anno_lock);
01704     if (file->anno && file->anno->size())
01705     {
01706       if (str_out.tell())
01707       {
01708         str_out.write((void *) "", 1);
01709       }
01710       file->anno->seek(0);
01711       str_out.copy(*file->anno);
01712     }
01713   } else if (file->is_data_present())
01714   {
01715            // Copy all anno chunks, but do NOT modify
01716            // DjVuFile::anno (to avoid correlation with DjVuFile::decode())
01717     const GP<ByteStream> str=file->data_pool->get_stream();
01718     const GP<IFFByteStream> giff=IFFByteStream::create(str);
01719     IFFByteStream &iff=*giff;
01720     GUTF8String chkid;
01721     if (iff.get_chunk(chkid))
01722     {
01723       while(iff.get_chunk(chkid))
01724       {
01725         if (is_annotation(chkid))
01726         {
01727           if (str_out.tell())
01728           {
01729             str_out.write((void *) "", 1);
01730           }
01731           const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01732           IFFByteStream &iff_out=*giff_out;
01733           iff_out.put_chunk(chkid);
01734           iff_out.copy(*iff.get_bytestream());
01735           iff_out.close_chunk();
01736         }
01737         iff.close_chunk();
01738       }
01739     }
01740     file->data_pool->clear_stream();
01741   }
01742 }
01743 
01744 void
01745 DjVuFile::get_text(
01746   const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01747 {
01748   DEBUG_MSG("DjVuFile::get_text()\n");
01749   ByteStream &str_out=*gstr_out;
01750   if (!file->is_data_present() ||
01751     file->is_modified() && file->text)
01752   {
01753     // Process the decoded (?) text
01754     GCriticalSectionLock lock(&file->text_lock);
01755     if (file->text && file->text->size())
01756     {
01757       if (str_out.tell())
01758       {
01759         str_out.write((void *) "", 1);
01760       }
01761       file->text->seek(0);
01762       str_out.copy(*file->text);
01763     }
01764   } else if (file->is_data_present())
01765   {
01766            // Copy all text chunks, but do NOT modify
01767            // DjVuFile::text (to avoid correlation with DjVuFile::decode())
01768     const GP<ByteStream> str=file->data_pool->get_stream();
01769     const GP<IFFByteStream> giff=IFFByteStream::create(str);
01770     IFFByteStream &iff=*giff;
01771     GUTF8String chkid;
01772     if (iff.get_chunk(chkid))
01773     {
01774       while(iff.get_chunk(chkid))
01775       {
01776         if (is_text(chkid))
01777         {
01778           if (str_out.tell())
01779           {
01780             str_out.write((void *) "", 1);
01781           }
01782           const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01783           IFFByteStream &iff_out=*giff_out;
01784           iff_out.put_chunk(chkid);
01785           iff_out.copy(*iff.get_bytestream());
01786           iff_out.close_chunk();
01787         }
01788         iff.close_chunk();
01789       }
01790     }
01791     file->data_pool->clear_stream();
01792   }
01793 }
01794 
01795 void
01796 DjVuFile::get_meta(
01797   const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01798 {
01799   DEBUG_MSG("DjVuFile::get_meta()\n");
01800   ByteStream &str_out=*gstr_out;
01801   if (!file->is_data_present() ||
01802     file->is_modified() && file->meta)
01803   {
01804     // Process the decoded (?) meta
01805     GCriticalSectionLock lock(&file->meta_lock);
01806     if (file->meta && file->meta->size())
01807     {
01808       if (str_out.tell())
01809       {
01810         str_out.write((void *) "", 1);
01811       }
01812       file->meta->seek(0);
01813       str_out.copy(*file->meta);
01814     }
01815   } else if (file->is_data_present())
01816   {
01817            // Copy all meta chunks, but do NOT modify
01818            // DjVuFile::meta (to avoid correlation with DjVuFile::decode())
01819     const GP<ByteStream> str=file->data_pool->get_stream();
01820     const GP<IFFByteStream> giff=IFFByteStream::create(str);
01821     IFFByteStream &iff=*giff;
01822     GUTF8String chkid;
01823     if (iff.get_chunk(chkid))
01824     {
01825       while(iff.get_chunk(chkid))
01826       {
01827         if (is_meta(chkid))
01828         {
01829           if (str_out.tell())
01830           {
01831             str_out.write((void *) "", 1);
01832           }
01833           const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01834           IFFByteStream &iff_out=*giff_out;
01835           iff_out.put_chunk(chkid);
01836           iff_out.copy(*iff.get_bytestream());
01837           iff_out.close_chunk();
01838         }
01839         iff.close_chunk();
01840       }
01841     }
01842     file->data_pool->clear_stream();
01843   }
01844 }
01845 
01846 GP<ByteStream>
01847 DjVuFile::get_anno(void)
01848 {
01849   DEBUG_MSG("DjVuFile::get_text(void)\n");
01850   GP<ByteStream> gstr(ByteStream::create());
01851   get_anno(this, gstr);
01852   ByteStream &str=*gstr;
01853   if (!str.tell())
01854   { 
01855     gstr=0;
01856   }else
01857   {
01858     str.seek(0);
01859   }
01860   return gstr;
01861 }
01862 
01863 GP<ByteStream>
01864 DjVuFile::get_text(void)
01865 {
01866   DEBUG_MSG("DjVuFile::get_text(void)\n");
01867   GP<ByteStream> gstr(ByteStream::create());
01868   get_text(this, gstr);
01869   ByteStream &str=*gstr;
01870   if (!str.tell())
01871   { 
01872     gstr=0;
01873   }else
01874   {
01875     str.seek(0);
01876   }
01877   return gstr;
01878 }
01879 
01880 GP<ByteStream>
01881 DjVuFile::get_meta(void)
01882 {
01883   DEBUG_MSG("DjVuFile::get_meta(void)\n");
01884   GP<ByteStream> gstr(ByteStream::create());
01885   get_meta(this, gstr);
01886   ByteStream &str=*gstr;
01887   if (!str.tell())
01888   { 
01889     gstr=0;
01890   }else
01891   {
01892     str.seek(0);
01893   }
01894   return gstr;
01895 }
01896 
01897 void
01898 DjVuFile::static_trigger_cb(void * cl_data)
01899 {
01900   DjVuFile * th=(DjVuFile *) cl_data;
01901   G_TRY {
01902     GP<DjVuPort> port=DjVuPort::get_portcaster()->is_port_alive(th);
01903     if (port && port->inherits("DjVuFile"))
01904       ((DjVuFile *) (DjVuPort *) port)->trigger_cb();
01905   } G_CATCH(exc) {
01906     G_TRY {
01907       get_portcaster()->notify_error(th, exc.get_cause());
01908     } G_CATCH_ALL {} G_ENDCATCH;
01909   } G_ENDCATCH;
01910 }
01911 
01912 void
01913 DjVuFile::trigger_cb(void)
01914 {
01915   GP<DjVuFile> life_saver=this;
01916   
01917   DEBUG_MSG("DjVuFile::trigger_cb(): got data for '" << url << "'\n");
01918   DEBUG_MAKE_INDENT(3);
01919   
01920   file_size=data_pool->get_length();
01921   flags|=DATA_PRESENT;
01922   get_portcaster()->notify_file_flags_changed(this, DATA_PRESENT, 0);
01923   
01924   if (!are_incl_files_created())
01925     process_incl_chunks();
01926   
01927   bool all=true;
01928   inc_files_lock.lock();
01929   GPList<DjVuFile> files_list=inc_files_list;
01930   inc_files_lock.unlock();
01931   for(GPosition pos=files_list;pos&&(all=files_list[pos]->is_all_data_present());++pos)
01932     EMPTY_LOOP;
01933   if (all)
01934   {
01935     DEBUG_MSG("DjVuFile::trigger_cb(): We have ALL data for '" << url << "'\n");
01936     flags|=ALL_DATA_PRESENT;
01937     get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
01938   }
01939 }
01940 
01941 void
01942 DjVuFile::progress_cb(int pos, void * cl_data)
01943 {
01944   DEBUG_MSG("DjVuFile::progress_cb() called\n");
01945   DEBUG_MAKE_INDENT(3);
01946   
01947   DjVuFile * th=(DjVuFile *) cl_data;
01948   
01949   int length=th->decode_data_pool->get_length();
01950   if (length>0)
01951   {
01952     float progress=(float) pos/length;
01953     DEBUG_MSG("progress=" << progress << "\n");
01954     get_portcaster()->notify_decode_progress(th, progress);
01955   } else
01956   {
01957     DEBUG_MSG("DataPool size is still unknown => ignoring\n");
01958   }
01959 }
01960 
01961 //*****************************************************************************
01962 //******************************** Utilities **********************************
01963 //*****************************************************************************
01964 
01965 void
01966 DjVuFile::move(GMap<GURL, void *> & map, const GURL & dir_url)
01967 // This function may block for data.
01968 {
01969   if (!map.contains(url))
01970   {
01971     map[url]=0;
01972     
01973     url=GURL::UTF8(url.name(),dir_url);
01974     
01975     
01976     // Leave the lock here!
01977     GCriticalSectionLock lock(&inc_files_lock);
01978     for(GPosition pos=inc_files_list;pos;++pos)
01979       inc_files_list[pos]->move(map, dir_url);
01980   }
01981 }
01982 
01983 void
01984 DjVuFile::move(const GURL & dir_url)
01985 // This function may block for data.
01986 {
01987   check();
01988   DEBUG_MSG("DjVuFile::move(): dir_url='" << dir_url << "'\n");
01989   DEBUG_MAKE_INDENT(3);
01990   
01991   GMap<GURL, void *> map;
01992   move(map, dir_url);
01993 }
01994 
01995 void
01996 DjVuFile::set_name(const GUTF8String &name)
01997 {
01998   DEBUG_MSG("DjVuFile::set_name(): name='" << name << "'\n");
01999   DEBUG_MAKE_INDENT(3);
02000   url=GURL::UTF8(name,url.base());
02001 }
02002 
02003 //*****************************************************************************
02004 //****************************** Data routines ********************************
02005 //*****************************************************************************
02006 
02007 int
02008 DjVuFile::get_chunks_number(void)
02009 {
02010   if(chunks_number < 0)
02011   {
02012     const GP<ByteStream> str(data_pool->get_stream());
02013     GUTF8String chkid;
02014     const GP<IFFByteStream> giff(IFFByteStream::create(str));
02015     IFFByteStream &iff=*giff;
02016     if (!iff.get_chunk(chkid))
02017       REPORT_EOF(true)
02018       
02019       int chunks=0;
02020     int last_chunk=0;
02021     G_TRY
02022     {
02023       int chksize;
02024       for(;(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02025       {
02026         chunks++;
02027         iff.seek_close_chunk();
02028       }
02029       chunks_number=last_chunk;
02030     }
02031     G_CATCH(ex)
02032     {
02033       chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02034       report_error(ex,(recover_errors<=SKIP_PAGES));
02035     }
02036     G_ENDCATCH;
02037     data_pool->clear_stream();
02038   }
02039   return chunks_number;
02040 }
02041 
02042 GUTF8String
02043 DjVuFile::get_chunk_name(int chunk_num)
02044 {
02045   if(chunk_num < 0)
02046   {
02047     G_THROW( ERR_MSG("DjVuFile.illegal_chunk") );
02048   }
02049   if((chunks_number >= 0)&&(chunk_num > chunks_number))
02050   {
02051     G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
02052   }
02053   check();
02054   
02055   GUTF8String name;
02056   const GP<ByteStream> str(data_pool->get_stream());
02057   GUTF8String chkid;
02058   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02059   IFFByteStream &iff=*giff;
02060   if (!iff.get_chunk(chkid)) 
02061     REPORT_EOF(true)
02062     
02063     int chunks=0;
02064   int last_chunk=0;
02065   G_TRY
02066   {
02067     int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02068     int chksize;
02069     for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02070     {
02071       if (chunks++==chunk_num) { name=chkid; break; }
02072       iff.seek_close_chunk();
02073     }
02074   }
02075   G_CATCH(ex)
02076   {
02077     if (chunks_number < 0)
02078       chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02079     report_error(ex,(recover_errors <= SKIP_PAGES));
02080   }
02081   G_ENDCATCH;
02082   if (!name.length())
02083   {
02084     if (chunks_number < 0) chunks_number=chunks;
02085     G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
02086   }
02087   return name;
02088 }
02089 
02090 bool
02091 DjVuFile::contains_chunk(const GUTF8String &chunk_name)
02092 {
02093   check();
02094   DEBUG_MSG("DjVuFile::contains_chunk(): url='" << url << "', chunk_name='" <<
02095     chunk_name << "'\n");
02096   DEBUG_MAKE_INDENT(3);
02097   
02098   bool contains=0;
02099   const GP<ByteStream> str(data_pool->get_stream());
02100   GUTF8String chkid;
02101   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02102   IFFByteStream &iff=*giff;
02103   if (!iff.get_chunk(chkid)) 
02104     REPORT_EOF((recover_errors<=SKIP_PAGES))
02105     
02106     int chunks=0;
02107   int last_chunk=0;
02108   G_TRY
02109   {
02110     int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02111     int chksize;
02112     for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02113     {
02114       chunks++;
02115       if (chkid==chunk_name) { contains=1; break; }
02116       iff.seek_close_chunk();
02117     }
02118     if (!contains &&(chunks_number < 0)) chunks_number=last_chunk;
02119   }
02120   G_CATCH(ex)
02121   {
02122     if (chunks_number < 0)
02123       chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02124     report_error(ex,(recover_errors <= SKIP_PAGES));
02125   }
02126   G_ENDCATCH;
02127   data_pool->clear_stream();
02128   return contains;
02129 }
02130 
02131 bool
02132 DjVuFile::contains_anno(void)
02133 {
02134   const GP<ByteStream> str(data_pool->get_stream());
02135   
02136   GUTF8String chkid;
02137   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02138   IFFByteStream &iff=*giff;
02139   if (!iff.get_chunk(chkid))
02140     G_THROW( ByteStream::EndOfFile );
02141   
02142   while(iff.get_chunk(chkid))
02143   {
02144     if (is_annotation(chkid))
02145       return true;
02146     iff.close_chunk();
02147   }
02148   
02149   data_pool->clear_stream();
02150   return false;
02151 }
02152 
02153 bool
02154 DjVuFile::contains_text(void)
02155 {
02156   const GP<ByteStream> str(data_pool->get_stream());
02157   
02158   GUTF8String chkid;
02159   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02160   IFFByteStream &iff=*giff;
02161   if (!iff.get_chunk(chkid))
02162     G_THROW( ByteStream::EndOfFile );
02163   
02164   while(iff.get_chunk(chkid))
02165   {
02166     if (is_text(chkid))
02167       return true;
02168     iff.close_chunk();
02169   }
02170   
02171   data_pool->clear_stream();
02172   return false;
02173 }
02174 
02175 bool
02176 DjVuFile::contains_meta(void)
02177 {
02178   const GP<ByteStream> str(data_pool->get_stream());
02179   
02180   GUTF8String chkid;
02181   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02182   IFFByteStream &iff=*giff;
02183   if (!iff.get_chunk(chkid))
02184     G_THROW( ByteStream::EndOfFile );
02185   
02186   while(iff.get_chunk(chkid))
02187   {
02188     if (is_meta(chkid))
02189       return true;
02190     iff.close_chunk();
02191   }
02192   
02193   data_pool->clear_stream();
02194   return false;
02195 }
02196 
02197 //*****************************************************************************
02198 //****************************** Save routines ********************************
02199 //*****************************************************************************
02200 
02201 static void
02202 copy_chunks(const GP<ByteStream> &from, IFFByteStream &ostr)
02203 {
02204   from->seek(0);
02205   const GP<IFFByteStream> giff(IFFByteStream::create(from));
02206   IFFByteStream &iff=*giff;
02207   GUTF8String chkid;
02208   int chksize;
02209   while ((chksize=iff.get_chunk(chkid)))
02210   {
02211     ostr.put_chunk(chkid);
02212     int ochksize=ostr.copy(*iff.get_bytestream());
02213     ostr.close_chunk();
02214     iff.seek_close_chunk();
02215     if(ochksize != chksize)
02216     {
02217       G_THROW( ByteStream::EndOfFile );
02218     }
02219   }
02220 }
02221 
02222 
02223 void
02224 DjVuFile::add_djvu_data(IFFByteStream & ostr, GMap<GURL, void *> & map,
02225                         const bool included_too, const bool no_ndir)
02226 {
02227   check();
02228   if (map.contains(url)) return;
02229   bool top_level = !map.size();
02230   map[url]=0;
02231   bool processed_annotation = false;
02232   bool processed_text = false;
02233   bool processed_meta = false;
02234   
02235   const GP<ByteStream> str(data_pool->get_stream());
02236   GUTF8String chkid;
02237   const GP<IFFByteStream> giff(IFFByteStream::create(str));
02238   IFFByteStream &iff=*giff;
02239   if (!iff.get_chunk(chkid)) 
02240     REPORT_EOF(true)
02241     
02242     // Open toplevel form
02243     if (top_level) 
02244       ostr.put_chunk(chkid);
02245     // Process chunks
02246     int chunks=0;
02247     int last_chunk=0;
02248     G_TRY
02249     {
02250       int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02251       int chksize;
02252       for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
02253       {
02254         chunks++;
02255         if (is_info(chkid) && info)
02256         {
02257           ostr.put_chunk(chkid);
02258           info->encode(*ostr.get_bytestream());
02259           ostr.close_chunk();
02260         }
02261         else if (chkid=="INCL" && included_too)
02262         {
02263           GP<DjVuFile> file = process_incl_chunk(*iff.get_bytestream());
02264           if (file)
02265           {
02266             if(recover_errors!=ABORT)
02267               file->set_recover_errors(recover_errors);
02268             if(verbose_eof)
02269               file->set_verbose_eof(verbose_eof);
02270             file->add_djvu_data(ostr, map, included_too, no_ndir);
02271           }
02272         } 
02273         else if (is_annotation(chkid) && anno && anno->size())
02274         {
02275           if (!processed_annotation)
02276           {
02277             processed_annotation = true;
02278             GCriticalSectionLock lock(&anno_lock);
02279             copy_chunks(anno, ostr);
02280           }
02281         }
02282         else if (is_text(chkid) && text && text->size())
02283         {
02284           if (!processed_text)
02285           {
02286             processed_text = true;
02287             GCriticalSectionLock lock(&text_lock);
02288             copy_chunks(text, ostr);
02289           }
02290         }
02291         else if (is_meta(chkid) && meta && meta->size())
02292         {
02293           if (!processed_meta)
02294           {
02295             processed_meta = true;
02296             GCriticalSectionLock lock(&meta_lock);
02297             copy_chunks(meta, ostr);
02298           }
02299         }
02300         else if (chkid!="NDIR"||!(no_ndir || dir))
02301         {  // Copy NDIR chunks, but never generate new ones.
02302           ostr.put_chunk(chkid);
02303           ostr.copy(*iff.get_bytestream());
02304           ostr.close_chunk();
02305         }
02306         iff.seek_close_chunk();
02307       }
02308       if (chunks_number < 0) chunks_number=last_chunk;
02309     }
02310     G_CATCH(ex)
02311     {
02312       if(!ex.cmp_cause(ByteStream::EndOfFile))
02313       {
02314         if (chunks_number < 0)
02315           chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02316         report_error(ex,(recover_errors<=SKIP_PAGES));
02317       }else
02318       {
02319         report_error(ex,true);
02320       }
02321     }
02322     G_ENDCATCH;
02323     
02324     // Otherwise, writes annotation at the end (annotations could be big)
02325     if (!processed_annotation && anno && anno->size())
02326     {
02327       processed_annotation = true;
02328       GCriticalSectionLock lock(&anno_lock);
02329       copy_chunks(anno, ostr);
02330     }
02331     if (!processed_text && text && text->size())
02332     {
02333       processed_text = true;
02334       GCriticalSectionLock lock(&text_lock);
02335       copy_chunks(text, ostr);
02336     }
02337     if (!processed_meta && meta && meta->size())
02338     {
02339       processed_meta = true;
02340       GCriticalSectionLock lock(&meta_lock);
02341       copy_chunks(meta, ostr);
02342     }
02343     // Close iff
02344     if (top_level) 
02345       ostr.close_chunk();
02346 
02347   data_pool->clear_stream();
02348 }
02349 
02350 GP<ByteStream>  
02351 DjVuFile::get_djvu_bytestream(const bool included_too, const bool no_ndir)
02352 {
02353    check();
02354    DEBUG_MSG("DjVuFile::get_djvu_bytestream(): creating DjVu raw file\n");
02355    DEBUG_MAKE_INDENT(3);
02356    const GP<ByteStream> pbs(ByteStream::create());
02357    const GP<IFFByteStream> giff=IFFByteStream::create(pbs);
02358    IFFByteStream &iff=*giff;
02359    GMap<GURL, void *> map;
02360    add_djvu_data(iff, map, included_too, no_ndir);
02361    iff.flush();
02362    pbs->seek(0, SEEK_SET);
02363    return pbs;
02364 }
02365 
02366 GP<DataPool>
02367 DjVuFile::get_djvu_data(const bool included_too, const bool no_ndir)
02368 {
02369   const GP<ByteStream> pbs = get_djvu_bytestream(included_too, no_ndir);
02370   return DataPool::create(pbs);
02371 }
02372 
02373 void
02374 DjVuFile::merge_anno(ByteStream &out)
02375 {
02376   // Reuse get_merged_anno(), which is better than the previous
02377   // implementation due to three things:
02378   //  1. It works even before the file is completely decoded
02379   //  2. It merges annotations taking into account where a child DjVuFile
02380   //     is included.
02381   //  3. It handles loops in DjVuFile's hierarchy
02382   
02383   const GP<ByteStream> str(get_merged_anno());
02384   if (str)
02385   {
02386     str->seek(0);
02387     if (out.tell())
02388     {
02389       out.write((void *) "", 1);
02390     }
02391     out.copy(*str);
02392   }
02393 }
02394 
02395 void
02396 DjVuFile::get_text(ByteStream &out)
02397 {
02398   const GP<ByteStream> str(get_text());
02399   if (str)
02400   {
02401     str->seek(0);
02402     if (out.tell())
02403     {
02404       out.write((void *) "", 1);
02405     }
02406     out.copy(*str);
02407   }
02408 }
02409 
02410 void
02411 DjVuFile::get_meta(ByteStream &out)
02412 {
02413   const GP<ByteStream> str(get_meta());
02414   if (str)
02415   {
02416     str->seek(0);
02417     if (out.tell())
02418     {
02419       out.write((void *) "", 1);
02420     }
02421     out.copy(*str);
02422   }
02423 }
02424 
02425 
02426 
02427 //****************************************************************************
02428 //******************************* Modifying **********************************
02429 //****************************************************************************
02430 
02431 void
02432 DjVuFile::remove_anno(void)
02433 {
02434   DEBUG_MSG("DjVuFile::remove_anno()\n");
02435   const GP<ByteStream> str_in(data_pool->get_stream());
02436   const GP<ByteStream> gstr_out(ByteStream::create());
02437   
02438   GUTF8String chkid;
02439   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02440   IFFByteStream &iff_in=*giff_in;
02441   if (!iff_in.get_chunk(chkid))
02442     G_THROW( ByteStream::EndOfFile );
02443   
02444   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02445   IFFByteStream &iff_out=*giff_out;
02446   iff_out.put_chunk(chkid);
02447   
02448   while(iff_in.get_chunk(chkid))
02449   {
02450     if (!is_annotation(chkid))
02451     {
02452       iff_out.put_chunk(chkid);
02453       iff_out.copy(*iff_in.get_bytestream());
02454       iff_out.close_chunk();
02455     }
02456     iff_in.close_chunk();
02457   }
02458   
02459   iff_out.close_chunk();
02460   
02461   gstr_out->seek(0, SEEK_SET);
02462   data_pool=DataPool::create(gstr_out);
02463   chunks_number=-1;
02464   
02465   anno=0;
02466   
02467   flags|=MODIFIED;
02468   data_pool->clear_stream();
02469 }
02470 
02471 void
02472 DjVuFile::remove_text(void)
02473 {
02474   DEBUG_MSG("DjVuFile::remove_text()\n");
02475   const GP<ByteStream> str_in(data_pool->get_stream());
02476   const GP<ByteStream> gstr_out(ByteStream::create());
02477   
02478   GUTF8String chkid;
02479   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02480   IFFByteStream &iff_in=*giff_in;
02481   if (!iff_in.get_chunk(chkid))
02482     G_THROW( ByteStream::EndOfFile );
02483   
02484   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02485   IFFByteStream &iff_out=*giff_out;
02486   iff_out.put_chunk(chkid);
02487   
02488   while(iff_in.get_chunk(chkid))
02489   {
02490     if (!is_text(chkid))
02491     {
02492       iff_out.put_chunk(chkid);
02493       iff_out.copy(*iff_in.get_bytestream());
02494       iff_out.close_chunk();
02495     }
02496     iff_in.close_chunk();
02497   }
02498   
02499   iff_out.close_chunk();
02500   
02501   gstr_out->seek(0, SEEK_SET);
02502   data_pool=DataPool::create(gstr_out);
02503   chunks_number=-1;
02504   
02505   text=0;
02506   
02507   flags|=MODIFIED;
02508   data_pool->clear_stream();
02509 }
02510 
02511 void
02512 DjVuFile::remove_meta(void)
02513 {
02514   DEBUG_MSG("DjVuFile::remove_meta()\n");
02515   const GP<ByteStream> str_in(data_pool->get_stream());
02516   const GP<ByteStream> gstr_out(ByteStream::create());
02517   
02518   GUTF8String chkid;
02519   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02520   IFFByteStream &iff_in=*giff_in;
02521   if (!iff_in.get_chunk(chkid))
02522     G_THROW( ByteStream::EndOfFile );
02523   
02524   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02525   IFFByteStream &iff_out=*giff_out;
02526   iff_out.put_chunk(chkid);
02527   
02528   while(iff_in.get_chunk(chkid))
02529   {
02530     if (!is_meta(chkid))
02531     {
02532       iff_out.put_chunk(chkid);
02533       iff_out.copy(*iff_in.get_bytestream());
02534       iff_out.close_chunk();
02535     }
02536     iff_in.close_chunk();
02537   }
02538   
02539   iff_out.close_chunk();
02540   
02541   gstr_out->seek(0, SEEK_SET);
02542   data_pool=DataPool::create(gstr_out);
02543   chunks_number=-1;
02544   
02545   meta=0;
02546   
02547   flags|=MODIFIED;
02548   data_pool->clear_stream();
02549 }
02550 
02551 void
02552 DjVuFile::rebuild_data_pool(void)
02553 {
02554   data_pool=get_djvu_data(false,false);
02555   chunks_number=1;
02556   flags|=MODIFIED;
02557 }
02558 
02559 // Do NOT comment this function out. It's used by DjVuDocEditor to convert
02560 // old-style DjVu documents to BUNDLED format.
02561 
02562 GP<DataPool>
02563 DjVuFile::unlink_file(const GP<DataPool> & data, const GUTF8String &name)
02564 // Will process contents of data[] and remove any INCL chunk
02565 // containing 'name'
02566 {
02567   DEBUG_MSG("DjVuFile::unlink_file()\n");
02568   const GP<ByteStream> gstr_out(ByteStream::create());
02569   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02570   IFFByteStream &iff_out=*giff_out;
02571   
02572   const GP<ByteStream> str_in(data->get_stream());
02573   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02574   IFFByteStream &iff_in=*giff_in;
02575   
02576   int chksize;
02577   GUTF8String chkid;
02578   if (!iff_in.get_chunk(chkid)) return data;
02579   
02580   iff_out.put_chunk(chkid);
02581   
02582   while((chksize=iff_in.get_chunk(chkid)))
02583   {
02584     if (chkid=="INCL")
02585     {
02586       GUTF8String incl_str;
02587       char buffer[1024];
02588       int length;
02589       while((length=iff_in.read(buffer, 1024)))
02590         incl_str+=GUTF8String(buffer, length);
02591       
02592       // Eat '\n' in the beginning and at the end
02593       while(incl_str.length() && incl_str[0]=='\n')
02594       {
02595         incl_str=incl_str.substr(1,(unsigned int)(-1));
02596       }
02597       while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
02598       {
02599         incl_str.setat(incl_str.length()-1, 0);
02600       }
02601       if (incl_str!=name)
02602       {
02603         iff_out.put_chunk(chkid);
02604         iff_out.get_bytestream()->writestring(incl_str);
02605         iff_out.close_chunk();
02606       }
02607     } else
02608     {
02609       iff_out.put_chunk(chkid);
02610       char buffer[1024];
02611       int length;
02612       for(const GP<ByteStream> gbs(iff_out.get_bytestream());
02613         (length=iff_in.read(buffer, 1024));)
02614       {
02615         gbs->writall(buffer, length);
02616       }
02617       iff_out.close_chunk();
02618     }
02619     iff_in.close_chunk();
02620   }
02621   iff_out.close_chunk();
02622   iff_out.flush();
02623   gstr_out->seek(0, SEEK_SET);
02624   data->clear_stream();
02625   return DataPool::create(gstr_out);
02626 }
02627 
02628 #ifndef NEED_DECODER_ONLY
02629 void
02630 DjVuFile::insert_file(const GUTF8String &id, int chunk_num)
02631 {
02632   DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "', chunk_num="
02633     << chunk_num << "\n");
02634   DEBUG_MAKE_INDENT(3);
02635   
02636   // First: create new data
02637   const GP<ByteStream> str_in(data_pool->get_stream());
02638   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02639   IFFByteStream &iff_in=*giff_in;
02640   
02641   const GP<ByteStream> gstr_out(ByteStream::create());
02642   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02643   IFFByteStream &iff_out=*giff_out;
02644   
02645   int chunk_cnt=0;
02646   bool done=false;
02647   GUTF8String chkid;
02648   if (iff_in.get_chunk(chkid))
02649   {
02650     iff_out.put_chunk(chkid);
02651     int chksize;
02652     while((chksize=iff_in.get_chunk(chkid)))
02653     {
02654       if (chunk_cnt++==chunk_num)
02655       {
02656         iff_out.put_chunk("INCL");
02657         iff_out.get_bytestream()->writestring(id);
02658         iff_out.close_chunk();
02659         done=true;
02660       }
02661       iff_out.put_chunk(chkid);
02662       iff_out.copy(*iff_in.get_bytestream());
02663       iff_out.close_chunk();
02664       iff_in.close_chunk();
02665     }
02666     if (!done)
02667     {
02668       iff_out.put_chunk("INCL");
02669       iff_out.get_bytestream()->writestring(id);
02670       iff_out.close_chunk();
02671     }
02672     iff_out.close_chunk();
02673   }
02674   gstr_out->seek(0, SEEK_SET);
02675   data_pool=DataPool::create(gstr_out);
02676   chunks_number=-1;
02677   
02678   // Second: create missing DjVuFiles
02679   process_incl_chunks();
02680   
02681   flags|=MODIFIED;
02682   data_pool->clear_stream();
02683 }
02684 #endif
02685 
02686 void
02687 DjVuFile::unlink_file(const GUTF8String &id)
02688 {
02689   DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "'\n");
02690   DEBUG_MAKE_INDENT(3);
02691   
02692   // Remove the file from the list of included files
02693   {
02694     GURL url=DjVuPort::get_portcaster()->id_to_url(this, id);
02695     if (url.is_empty()) url=GURL::UTF8(id,this->url.base());
02696     GCriticalSectionLock lock(&inc_files_lock);
02697     for(GPosition pos=inc_files_list;pos;)
02698       if (inc_files_list[pos]->get_url()==url)
02699       {
02700         GPosition this_pos=pos;
02701         ++pos;
02702         inc_files_list.del(this_pos);
02703       } else ++pos;
02704   }
02705   
02706   // And update the data.
02707   const GP<ByteStream> str_in(data_pool->get_stream());
02708   const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02709   IFFByteStream &iff_in=*giff_in;
02710   
02711   const GP<ByteStream> gstr_out(ByteStream::create());
02712   const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02713   IFFByteStream &iff_out=*giff_out;
02714   
02715   GUTF8String chkid;
02716   if (iff_in.get_chunk(chkid))
02717   {
02718     iff_out.put_chunk(chkid);
02719     int chksize;
02720     while((chksize=iff_in.get_chunk(chkid)))
02721     {
02722       if (chkid!="INCL")
02723       {
02724         iff_out.put_chunk(chkid);
02725         iff_out.copy(*iff_in.get_bytestream());
02726         iff_out.close_chunk();
02727       } else
02728       {
02729         GUTF8String incl_str;
02730         char buffer[1024];
02731         int length;
02732         while((length=iff_in.read(buffer, 1024)))
02733           incl_str+=GUTF8String(buffer, length);
02734         
02735            // Eat '\n' in the beginning and at the end
02736         while(incl_str.length() && incl_str[0]=='\n')
02737         {
02738           incl_str=incl_str.substr(1,(unsigned int)(-1));
02739         }
02740         while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
02741           incl_str.setat(incl_str.length()-1, 0);
02742         if (incl_str!=id)
02743         {
02744           iff_out.put_chunk("INCL");
02745           iff_out.get_bytestream()->writestring(incl_str);
02746           iff_out.close_chunk();
02747         }
02748       }
02749       iff_in.close_chunk();
02750     }
02751     iff_out.close_chunk();
02752   }
02753   
02754   gstr_out->seek(0, SEEK_SET);
02755   data_pool=DataPool::create(gstr_out);
02756   chunks_number=-1;
02757   
02758   flags|=MODIFIED;
02759 }
02760 
02761 void
02762 DjVuFile::change_info(GP<DjVuInfo> xinfo,const bool do_reset)
02763 {
02764   DEBUG_MSG("DjVuFile::change_text()\n");
02765   // Mark this as modified
02766   set_modified(true);
02767   if(do_reset)
02768     reset();
02769   info=xinfo;
02770 }
02771 
02772 #ifndef NEED_DECODER_ONLY
02773 void
02774 DjVuFile::change_text(GP<DjVuTXT> txt,const bool do_reset)
02775 {
02776   DEBUG_MSG("DjVuFile::change_text()\n");
02777   GP<DjVuText> gtext_c=DjVuText::create();
02778   DjVuText &text_c=*gtext_c;
02779   if(contains_text())
02780   {
02781     const GP<ByteStream> file_text(get_text());
02782     if(file_text)
02783     {
02784       text_c.decode(file_text);
02785     }
02786   }
02787   GCriticalSectionLock lock(&text_lock);
02788   // Mark this as modified
02789   set_modified(true);
02790   if(do_reset)
02791     reset();
02792   text_c.txt = txt;
02793   text=ByteStream::create();
02794   text_c.encode(text);
02795 }
02796 
02797 void
02798 DjVuFile::change_meta(const GUTF8String &xmeta,const bool do_reset)
02799 {
02800   DEBUG_MSG("DjVuFile::change_meta()\n");
02801   // Mark this as modified
02802   set_modified(true);
02803   if(contains_meta())
02804   {
02805     (void)get_meta();
02806   }
02807   if(do_reset)
02808     reset();
02809   GCriticalSectionLock lock(&meta_lock);
02810   meta=ByteStream::create();
02811   if(xmeta.length())
02812   {
02813     const GP<IFFByteStream> giff=IFFByteStream::create(meta);
02814     IFFByteStream &iff=*giff;
02815     iff.put_chunk("METz");
02816     {
02817       GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50);
02818       gbsiff->writestring(xmeta);
02819     }
02820     iff.close_chunk();
02821   }
02822 }
02823 #endif
02824 
02825 
02826 #ifdef HAVE_NAMESPACES
02827 }
02828 # ifndef NOT_USING_DJVU_NAMESPACE
02829 using namespace DJVU;
02830 # endif
02831 #endif

kviewshell

Skip menu "kviewshell"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • kviewshell
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal