Blame libdjvu/DjVuFile.cpp

Packit df99a1
//C-  -*- C++ -*-
Packit df99a1
//C- -------------------------------------------------------------------
Packit df99a1
//C- DjVuLibre-3.5
Packit df99a1
//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
Packit df99a1
//C- Copyright (c) 2001  AT&T
Packit df99a1
//C-
Packit df99a1
//C- This software is subject to, and may be distributed under, the
Packit df99a1
//C- GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- or (at your option) any later version. The license should have
Packit df99a1
//C- accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C-
Packit df99a1
//C- This program is distributed in the hope that it will be useful,
Packit df99a1
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit df99a1
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit df99a1
//C- GNU General Public License for more details.
Packit df99a1
//C- 
Packit df99a1
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
Packit df99a1
//C- Lizardtech Software.  Lizardtech Software has authorized us to
Packit df99a1
//C- replace the original DjVu(r) Reference Library notice by the following
Packit df99a1
//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
Packit df99a1
//C-
Packit df99a1
//C-  ------------------------------------------------------------------
Packit df99a1
//C- | DjVu (r) Reference Library (v. 3.5)
Packit df99a1
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
Packit df99a1
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
Packit df99a1
//C- | 6,058,214 and patents pending.
Packit df99a1
//C- |
Packit df99a1
//C- | This software is subject to, and may be distributed under, the
Packit df99a1
//C- | GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- | or (at your option) any later version. The license should have
Packit df99a1
//C- | accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- | from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C- |
Packit df99a1
//C- | The computer code originally released by LizardTech under this
Packit df99a1
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
Packit df99a1
//C- | ORIGINAL CODE."  Subject to any third party intellectual property
Packit df99a1
//C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
Packit df99a1
//C- | non-exclusive license to make, use, sell, or otherwise dispose of 
Packit df99a1
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
Packit df99a1
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
Packit df99a1
//C- | General Public License.   This grant only confers the right to 
Packit df99a1
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
Packit df99a1
//C- | the extent such infringement is reasonably necessary to enable 
Packit df99a1
//C- | recipient to make, have made, practice, sell, or otherwise dispose 
Packit df99a1
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
Packit df99a1
//C- | any greater extent that may be necessary to utilize further 
Packit df99a1
//C- | modifications or combinations.
Packit df99a1
//C- |
Packit df99a1
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
Packit df99a1
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
Packit df99a1
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
Packit df99a1
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Packit df99a1
//C- +------------------------------------------------------------------
Packit df99a1
Packit df99a1
#ifdef HAVE_CONFIG_H
Packit df99a1
# include "config.h"
Packit df99a1
#endif
Packit df99a1
#if NEED_GNUG_PRAGMAS
Packit df99a1
# pragma implementation
Packit df99a1
#endif
Packit df99a1
Packit df99a1
#include "DjVuFile.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "MMRDecoder.h"
Packit df99a1
#ifdef NEED_JPEG_DECODER
Packit df99a1
#include "JPEGDecoder.h"
Packit df99a1
#endif
Packit df99a1
#include "DjVuAnno.h"
Packit df99a1
#include "DjVuText.h"
Packit df99a1
#include "DataPool.h"
Packit df99a1
#include "JB2Image.h"
Packit df99a1
#include "IW44Image.h"
Packit df99a1
#include "DjVuNavDir.h"
Packit df99a1
#ifndef NEED_DECODER_ONLY
Packit df99a1
#include "BSByteStream.h"
Packit df99a1
#endif // NEED_DECODER_ONLY
Packit df99a1
Packit df99a1
#include "debug.h"
Packit df99a1
Packit df99a1
Packit df99a1
#ifdef HAVE_NAMESPACES
Packit df99a1
namespace DJVU {
Packit df99a1
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
#endif
Packit df99a1
Packit df99a1
Packit df99a1
#define STRINGIFY(x) STRINGIFY_(x)
Packit df99a1
#define STRINGIFY_(x) #x
Packit df99a1
Packit df99a1
Packit df99a1
#define REPORT_EOF(x) \
Packit df99a1
  {G_TRY{G_THROW( ByteStream::EndOfFile );}G_CATCH(ex){report_error(ex,(x));}G_ENDCATCH;}
Packit df99a1
Packit df99a1
static GP<GPixmap> (*djvu_decode_codec)(ByteStream &bs)=0;
Packit df99a1
Packit df99a1
class ProgressByteStream : public ByteStream
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
  ProgressByteStream(const GP<ByteStream> & xstr) : str(xstr),
Packit df99a1
    last_call_pos(0) {}
Packit df99a1
  virtual ~ProgressByteStream() {}
Packit df99a1
Packit df99a1
  virtual size_t read(void *buffer, size_t size)
Packit df99a1
  {
Packit df99a1
    int rc=0;
Packit df99a1
	   // G_TRY {} CATCH; block here is merely to avoid egcs internal error
Packit df99a1
    G_TRY {
Packit df99a1
      int cur_pos=str->tell();
Packit df99a1
      if (progress_cb && (last_call_pos/256!=cur_pos/256))
Packit df99a1
      {
Packit df99a1
        progress_cb(cur_pos, progress_cl_data);
Packit df99a1
        last_call_pos=cur_pos;
Packit df99a1
      }
Packit df99a1
      rc=str->read(buffer, size);
Packit df99a1
    } G_CATCH_ALL {
Packit df99a1
      G_RETHROW;
Packit df99a1
    } G_ENDCATCH;
Packit df99a1
    return rc;
Packit df99a1
  }
Packit df99a1
  virtual size_t write(const void *buffer, size_t size)
Packit df99a1
  {
Packit df99a1
    return str->write(buffer, size);
Packit df99a1
  }
Packit df99a1
  virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false)
Packit df99a1
  {
Packit df99a1
    return str->seek(offset, whence);
Packit df99a1
  }
Packit df99a1
  virtual long tell(void ) const { return str->tell(); }
Packit df99a1
Packit df99a1
  void		set_progress_cb(void (* xprogress_cb)(int, void *),
Packit df99a1
  void * xprogress_cl_data)
Packit df99a1
  {
Packit df99a1
    progress_cb=xprogress_cb;
Packit df99a1
    progress_cl_data=xprogress_cl_data;
Packit df99a1
  }
Packit df99a1
private:
Packit df99a1
  GP<ByteStream> str;
Packit df99a1
  void		* progress_cl_data;
Packit df99a1
  void		(* progress_cb)(int pos, void *);
Packit df99a1
  int		last_call_pos;
Packit df99a1
  
Packit df99a1
  // Cancel C++ default stuff
Packit df99a1
  ProgressByteStream & operator=(const ProgressByteStream &);
Packit df99a1
};
Packit df99a1
Packit df99a1
Packit df99a1
DjVuFile::DjVuFile()
Packit df99a1
: file_size(0), recover_errors(ABORT), verbose_eof(false), chunks_number(-1),
Packit df99a1
initialized(false)
Packit df99a1
{
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::check() const
Packit df99a1
{
Packit df99a1
  if (!initialized)
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.not_init") );
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuFile::create(
Packit df99a1
  const GP<ByteStream> & str, const ErrorRecoveryAction recover_errors,
Packit df99a1
  const bool verbose_eof )
Packit df99a1
{
Packit df99a1
  DjVuFile *file=new DjVuFile();
Packit df99a1
  GP<DjVuFile> retval=file;
Packit df99a1
  file->set_recover_errors(recover_errors);
Packit df99a1
  file->set_verbose_eof(verbose_eof);
Packit df99a1
  file->init(str);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
DjVuFile::init(const GP<ByteStream> & str)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::DjVuFile(): ByteStream constructor\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  if (initialized)
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.2nd_init") );
Packit df99a1
  if (!get_count())
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.not_secured") );
Packit df99a1
  
Packit df99a1
  file_size=0;
Packit df99a1
  decode_thread=0;
Packit df99a1
  
Packit df99a1
  // Read the data from the stream
Packit df99a1
  data_pool=DataPool::create(str);
Packit df99a1
  
Packit df99a1
  // Construct some dummy URL
Packit df99a1
  GUTF8String buffer;
Packit df99a1
  buffer.format("djvufile:/%p.djvu", this);
Packit df99a1
  DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)buffer<<"\n");
Packit df99a1
  url=GURL::UTF8(buffer);
Packit df99a1
  
Packit df99a1
  // Set it here because trigger will call other DjVuFile's functions
Packit df99a1
  initialized=true;
Packit df99a1
  
Packit df99a1
  // Add (basically - call) the trigger
Packit df99a1
  data_pool->add_trigger(-1, static_trigger_cb, this);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuFile::create(
Packit df99a1
  const GURL & xurl, GP<DjVuPort> port, 
Packit df99a1
  const ErrorRecoveryAction recover_errors, const bool verbose_eof ) 
Packit df99a1
{
Packit df99a1
  DjVuFile *file=new DjVuFile();
Packit df99a1
  GP<DjVuFile> retval=file;
Packit df99a1
  file->set_recover_errors(recover_errors);
Packit df99a1
  file->set_verbose_eof(verbose_eof);
Packit df99a1
  file->init(xurl,port);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::init(const GURL & xurl, GP<DjVuPort> port) 
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::init(): url='" << xurl << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  if (initialized)
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.2nd_init") );
Packit df99a1
  if (!get_count())
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.not_secured") );
Packit df99a1
  if (xurl.is_empty())
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.empty_URL") );
Packit df99a1
  
Packit df99a1
  url = xurl;
Packit df99a1
  DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)url<<"\n");
Packit df99a1
  file_size=0;
Packit df99a1
  decode_thread=0;
Packit df99a1
  
Packit df99a1
  DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
  
Packit df99a1
  // We need it 'cause we're waiting for our own termination in stop_decode()
Packit df99a1
  pcaster->add_route(this, this);
Packit df99a1
  if (!port)
Packit df99a1
    port = simple_port = new DjVuSimplePort();
Packit df99a1
  pcaster->add_route(this, port);
Packit df99a1
  
Packit df99a1
  // Set it here because trigger will call other DjVuFile's functions
Packit df99a1
  initialized=true;
Packit df99a1
  
Packit df99a1
  if (!(data_pool=DataPool::create(pcaster->request_data(this, url))))
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.no_data") "\t"+url.get_string());
Packit df99a1
  data_pool->add_trigger(-1, static_trigger_cb, this);
Packit df99a1
}
Packit df99a1
Packit df99a1
DjVuFile::~DjVuFile(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::~DjVuFile(): destroying...\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  // No more messages. They may result in adding this file to a cache
Packit df99a1
  // which will be very-very bad as we're being destroyed
Packit df99a1
  get_portcaster()->del_port(this);
Packit df99a1
  
Packit df99a1
  // Unregister the trigger (we don't want it to be called and attempt
Packit df99a1
  // to access the destroyed object)
Packit df99a1
  if (data_pool)
Packit df99a1
    data_pool->del_trigger(static_trigger_cb, this);
Packit df99a1
  
Packit df99a1
  // We don't have to wait for decoding to finish here. It's already
Packit df99a1
  // finished (we know it because there is a "life saver" in the
Packit df99a1
  // thread function)  -- but we need to delete it
Packit df99a1
  delete decode_thread; decode_thread=0;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::reset(void)
Packit df99a1
{
Packit df99a1
   flags.enter();
Packit df99a1
   info = 0; 
Packit df99a1
   anno = 0; 
Packit df99a1
   text = 0; 
Packit df99a1
   meta = 0; 
Packit df99a1
   bg44 = 0; 
Packit df99a1
   fgbc = 0;
Packit df99a1
   fgjb = 0; 
Packit df99a1
   fgjd = 0;
Packit df99a1
   fgpm = 0;
Packit df99a1
   dir  = 0; 
Packit df99a1
   description = ""; 
Packit df99a1
   mimetype = "";
Packit df99a1
   flags=(flags&(ALL_DATA_PRESENT|DECODE_STOPPED|DECODE_FAILED));
Packit df99a1
   flags.leave();
Packit df99a1
}
Packit df99a1
Packit df99a1
unsigned int
Packit df99a1
DjVuFile::get_memory_usage(void) const
Packit df99a1
{
Packit df99a1
   unsigned int size=sizeof(*this);
Packit df99a1
   if (info) size+=info->get_memory_usage();
Packit df99a1
   if (bg44) size+=bg44->get_memory_usage();
Packit df99a1
   if (fgjb) size+=fgjb->get_memory_usage();
Packit df99a1
   if (fgpm) size+=fgpm->get_memory_usage();
Packit df99a1
   if (fgbc) size+=fgbc->size()*sizeof(int);
Packit df99a1
   if (anno) size+=anno->size();
Packit df99a1
   if (meta) size+=meta->size();
Packit df99a1
   if (dir) size+=dir->get_memory_usage();
Packit df99a1
   return size;
Packit df99a1
}
Packit df99a1
Packit df99a1
GPList<DjVuFile>
Packit df99a1
DjVuFile::get_included_files(bool only_created)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  if (!only_created && !are_incl_files_created())
Packit df99a1
    process_incl_chunks();
Packit df99a1
  
Packit df99a1
  GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
  GPList<DjVuFile> list=inc_files_list;	// Get a copy when locked
Packit df99a1
  return list;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::wait_for_chunk(void)
Packit df99a1
// Will return after a chunk has been decoded
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::wait_for_chunk() called\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  chunk_mon.enter();
Packit df99a1
  chunk_mon.wait();
Packit df99a1
  chunk_mon.leave();
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::wait_for_finish(bool self)
Packit df99a1
// if self==TRUE, will block until decoding of this file is over
Packit df99a1
// if self==FALSE, will block until decoding of a child (direct
Packit df99a1
// or indirect) is over.
Packit df99a1
// Will return FALSE if there is nothing to wait for. TRUE otherwise
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::wait_for_finish():  self=" << self <<"\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  if (self)
Packit df99a1
  {
Packit df99a1
    // It's best to check for self termination using flags. The reason
Packit df99a1
    // is that finish_mon is updated in a DjVuPort function, which
Packit df99a1
    // will not be called if the object is being destroyed
Packit df99a1
    GMonitorLock lock(&flags);
Packit df99a1
    if (is_decoding())
Packit df99a1
    {
Packit df99a1
      while(is_decoding()) flags.wait();
Packit df99a1
      DEBUG_MSG("got it\n");
Packit df99a1
      return 1;
Packit df99a1
    }
Packit df99a1
  } else
Packit df99a1
  {
Packit df99a1
    // By locking the monitor, we guarantee that situation doesn't change
Packit df99a1
    // between the moments when we check for pending finish events
Packit df99a1
    // and when we actually run wait(). If we don't lock, the last child
Packit df99a1
    // may terminate in between, and we'll wait forever.
Packit df99a1
    //
Packit df99a1
    // Locking is required by GMonitor interface too, btw.
Packit df99a1
    GMonitorLock lock(&finish_mon);
Packit df99a1
    GP<DjVuFile> file;
Packit df99a1
    {
Packit df99a1
      GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
      for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
        GP<DjVuFile> & f=inc_files_list[pos];
Packit df99a1
        if (f->is_decoding())
Packit df99a1
        {
Packit df99a1
          file=f; break;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
    if (file)
Packit df99a1
    {
Packit df99a1
      finish_mon.wait();
Packit df99a1
      DEBUG_MSG("got it\n");
Packit df99a1
      return 1;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  DEBUG_MSG("nothing to wait for\n");
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::notify_chunk_done(const DjVuPort *, const GUTF8String &)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  chunk_mon.enter();
Packit df99a1
  chunk_mon.broadcast();
Packit df99a1
  chunk_mon.leave();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::notify_file_flags_changed(const DjVuFile * src,
Packit df99a1
                                    long set_mask, long clr_mask)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  if (set_mask & (DECODE_OK | DECODE_FAILED | DECODE_STOPPED))
Packit df99a1
  {
Packit df99a1
    // Signal threads waiting for file termination
Packit df99a1
    finish_mon.enter();
Packit df99a1
    finish_mon.broadcast();
Packit df99a1
    finish_mon.leave();
Packit df99a1
    
Packit df99a1
    // In case a thread is still waiting for a chunk
Packit df99a1
    chunk_mon.enter();
Packit df99a1
    chunk_mon.broadcast();
Packit df99a1
    chunk_mon.leave();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  if ((set_mask & ALL_DATA_PRESENT) && src!=this &&
Packit df99a1
    are_incl_files_created() && is_data_present())
Packit df99a1
  {
Packit df99a1
    if (src!=this && are_incl_files_created() && is_data_present())
Packit df99a1
    {
Packit df99a1
      // Check if all children have data
Packit df99a1
      bool all=true;
Packit df99a1
      {
Packit df99a1
        GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
        for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
          if (!inc_files_list[pos]->is_all_data_present())
Packit df99a1
          {
Packit df99a1
            all=false;
Packit df99a1
            break;
Packit df99a1
          }
Packit df99a1
      }
Packit df99a1
      if (all)
Packit df99a1
      {
Packit df99a1
        DEBUG_MSG("Just got ALL data for '" << url << "'\n");
Packit df99a1
        flags|=ALL_DATA_PRESENT;
Packit df99a1
        get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::static_decode_func(void * cl_data)
Packit df99a1
{
Packit df99a1
  DjVuFile * th=(DjVuFile *) cl_data;
Packit df99a1
  
Packit df99a1
  /* Please do not undo this life saver. If you do then try to resolve the
Packit df99a1
  following conflict first:
Packit df99a1
  1. Decoding starts and there is only one external reference
Packit df99a1
  to the DjVuFile.
Packit df99a1
  2. Decoding proceeds and calls DjVuPortcaster::notify_error(),
Packit df99a1
  which creates inside a temporary GP<DjVuFile>.
Packit df99a1
  3. While notify_error() is running, the only external reference
Packit df99a1
  is lost, but the DjVuFile is still alive (remember the
Packit df99a1
  temporary GP<>?)
Packit df99a1
  4. The notify_error() returns, the temporary GP<> gets destroyed
Packit df99a1
  and the DjVuFile is attempting to destroy right in the middle
Packit df99a1
  of the decoding thread. This is either a dead block (waiting
Packit df99a1
  for the termination of the decoding from the ~DjVuFile() called
Packit df99a1
  from the decoding thread) or coredump. */
Packit df99a1
  GP<DjVuFile> life_saver=th;
Packit df99a1
  th->decode_life_saver=0;
Packit df99a1
  G_TRY {
Packit df99a1
    th->decode_func();
Packit df99a1
  } G_CATCH_ALL {
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::decode_func(void)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::decode_func() called, url='" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
  
Packit df99a1
  G_TRY {
Packit df99a1
    const GP<ByteStream> decode_stream(decode_data_pool->get_stream());
Packit df99a1
    ProgressByteStream *pstr=new ProgressByteStream(decode_stream);
Packit df99a1
    const GP<ByteStream> gpstr(pstr);
Packit df99a1
    pstr->set_progress_cb(progress_cb, this);
Packit df99a1
    
Packit df99a1
    decode(gpstr);
Packit df99a1
    
Packit df99a1
    // Wait for all child files to finish
Packit df99a1
    while(wait_for_finish(0))
Packit df99a1
    	continue;
Packit df99a1
    
Packit df99a1
    DEBUG_MSG("waiting for children termination\n");
Packit df99a1
    // Check for termination status
Packit df99a1
    GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
    for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GP<DjVuFile> & f=inc_files_list[pos];
Packit df99a1
      if (f->is_decode_failed())
Packit df99a1
	G_THROW( ERR_MSG("DjVuFile.decode_fail") );
Packit df99a1
      if (f->is_decode_stopped())
Packit df99a1
	G_THROW( DataPool::Stop );
Packit df99a1
      if (!f->is_decode_ok())
Packit df99a1
	{
Packit df99a1
	  DEBUG_MSG("this_url='" << url << "'\n");
Packit df99a1
	  DEBUG_MSG("incl_url='" << f->get_url() << "'\n");
Packit df99a1
	  DEBUG_MSG("decoding=" << f->is_decoding() << "\n");
Packit df99a1
	  DEBUG_MSG("status='" << f->get_flags() << "\n");
Packit df99a1
	  G_THROW( ERR_MSG("DjVuFile.not_finished") );
Packit df99a1
	}
Packit df99a1
    }
Packit df99a1
  } G_CATCH(exc) {
Packit df99a1
    G_TRY {
Packit df99a1
      if (!exc.cmp_cause(DataPool::Stop))
Packit df99a1
	{
Packit df99a1
	  flags.enter();
Packit df99a1
	  flags = (flags & ~DECODING) | DECODE_STOPPED;
Packit df99a1
	  flags.leave();
Packit df99a1
	  pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.stopped"))
Packit df99a1
				 + GUTF8String("\t") + GUTF8String(url));
Packit df99a1
	  pcaster->notify_file_flags_changed(this, DECODE_STOPPED, DECODING);
Packit df99a1
	} else
Packit df99a1
	{
Packit df99a1
	  flags.enter();
Packit df99a1
	  flags = (flags & ~DECODING) | DECODE_FAILED;
Packit df99a1
	  flags.leave();
Packit df99a1
	  pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.failed"))
Packit df99a1
				 + GUTF8String("\t") + GUTF8String(url));
Packit df99a1
	  pcaster->notify_error(this, exc.get_cause());
Packit df99a1
	  pcaster->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
Packit df99a1
	}
Packit df99a1
    } G_CATCH_ALL
Packit df99a1
	{
Packit df99a1
	  DEBUG_MSG("******* Oops. Almost missed an exception\n");
Packit df99a1
	} G_ENDCATCH;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
Packit df99a1
  decode_data_pool->clear_stream();
Packit df99a1
  G_TRY {
Packit df99a1
    if (flags.test_and_modify(DECODING, 0, DECODE_OK | INCL_FILES_CREATED, DECODING))
Packit df99a1
      pcaster->notify_file_flags_changed(this, DECODE_OK | INCL_FILES_CREATED, 
Packit df99a1
                                         DECODING);
Packit df99a1
  } G_CATCH_ALL {} G_ENDCATCH;
Packit df99a1
  DEBUG_MSG("decoding thread for url='" << url << "' ended\n");
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuFile::process_incl_chunk(ByteStream & str, int file_num)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::process_incl_chunk(): processing INCL chunk...\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
  
Packit df99a1
  GUTF8String incl_str;
Packit df99a1
  char buffer[1024];
Packit df99a1
  int length;
Packit df99a1
  while((length=str.read(buffer, 1024)))
Packit df99a1
    incl_str+=GUTF8String(buffer, length);
Packit df99a1
  
Packit df99a1
  // Eat '\n' in the beginning and at the end
Packit df99a1
  while(incl_str.length() && incl_str[0]=='\n')
Packit df99a1
  {
Packit df99a1
    incl_str=incl_str.substr(1,(unsigned int)(-1));
Packit df99a1
  }
Packit df99a1
  while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
Packit df99a1
  {
Packit df99a1
    incl_str.setat(incl_str.length()-1, 0);
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  if (incl_str.length()>0)
Packit df99a1
  {
Packit df99a1
    if (strchr(incl_str, '/'))
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.malformed") );
Packit df99a1
    
Packit df99a1
    DEBUG_MSG("incl_str='" << incl_str << "'\n");
Packit df99a1
    
Packit df99a1
    GURL incl_url=pcaster->id_to_url(this, incl_str);
Packit df99a1
    if (incl_url.is_empty())	// Fallback. Should never be used.
Packit df99a1
      incl_url=GURL::UTF8(incl_str,url.base());
Packit df99a1
    
Packit df99a1
    // Now see if there is already a file with this *name* created
Packit df99a1
    {
Packit df99a1
      GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
      GPosition pos;
Packit df99a1
      for(pos=inc_files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
        if (inc_files_list[pos]->url.fname()==incl_url.fname())
Packit df99a1
           break;
Packit df99a1
      }
Packit df99a1
      if (pos)
Packit df99a1
        return inc_files_list[pos];
Packit df99a1
    }
Packit df99a1
    
Packit df99a1
    // No. We have to request a new file
Packit df99a1
    GP<DjVuFile> file;
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
      file=pcaster->id_to_file(this, incl_str);
Packit df99a1
    }
Packit df99a1
    G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      unlink_file(incl_str);
Packit df99a1
      // In order to keep compatibility with the previous
Packit df99a1
      // release of the DjVu plugin, we will not interrupt
Packit df99a1
      // decoding here. We will just report the error.
Packit df99a1
      // NOTE, that it's now the responsibility of the
Packit df99a1
      // decoder to resolve all chunk dependencies, and
Packit df99a1
      // abort decoding if necessary.
Packit df99a1
      
Packit df99a1
      get_portcaster()->notify_error(this,ex.get_cause());
Packit df99a1
      return 0;
Packit df99a1
    }
Packit df99a1
    G_ENDCATCH;
Packit df99a1
    if (!file)
Packit df99a1
    {
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.no_create") "\t"+incl_str);
Packit df99a1
    }
Packit df99a1
    if (recover_errors!=ABORT)
Packit df99a1
      file->set_recover_errors(recover_errors);
Packit df99a1
    if (verbose_eof)
Packit df99a1
      file->set_verbose_eof(verbose_eof);
Packit df99a1
    pcaster->add_route(file, this);
Packit df99a1
    
Packit df99a1
    // We may have been stopped. Make sure the child will be stopped too.
Packit df99a1
    if (flags & STOPPED)
Packit df99a1
      file->stop(false);
Packit df99a1
    if (flags & BLOCKED_STOPPED)
Packit df99a1
      file->stop(true);
Packit df99a1
    
Packit df99a1
    // Lock the list again and check if the file has already been
Packit df99a1
    // added by someone else
Packit df99a1
    {
Packit df99a1
      GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
      GPosition pos;
Packit df99a1
      for(pos=inc_files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
        if (inc_files_list[pos]->url.fname()==incl_url.fname())
Packit df99a1
          break;
Packit df99a1
      }
Packit df99a1
      if (pos)
Packit df99a1
      {
Packit df99a1
        file=inc_files_list[pos];
Packit df99a1
      } else if (file_num<0 || !(pos=inc_files_list.nth(file_num)))
Packit df99a1
      {
Packit df99a1
        inc_files_list.append(file);
Packit df99a1
      } else 
Packit df99a1
      {
Packit df99a1
        inc_files_list.insert_before(pos, file);
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
    return file;
Packit df99a1
  }
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::report_error(const GException &ex,bool throw_errors)
Packit df99a1
{
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
  if((!verbose_eof)|| (ex.cmp_cause(ByteStream::EndOfFile)))
Packit df99a1
    {
Packit df99a1
      if(throw_errors)
Packit df99a1
        G_RETHROW(ex);
Packit df99a1
      else
Packit df99a1
        get_portcaster()->notify_error(this,ex.get_cause());
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      GURL url=get_url();
Packit df99a1
      GUTF8String url_str=url.get_string();
Packit df99a1
      GUTF8String msg = GUTF8String( ERR_MSG("DjVuFile.EOF") "\t") + url;
Packit df99a1
      if(throw_errors)
Packit df99a1
        G_RETHROW(GException(msg,ex.get_file(),ex.get_line(),ex.get_function()));
Packit df99a1
      else
Packit df99a1
        get_portcaster()->notify_error(this,msg);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::process_incl_chunks(void)
Packit df99a1
// This function may block for data
Packit df99a1
// NOTE: It may be called again when INCL_FILES_CREATED is set.
Packit df99a1
// It happens in insert_file() when it has modified the data
Packit df99a1
// and wants to create the actual file
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::process_incl_chunks(void)\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  int incl_cnt=0;
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (iff.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    int chunks=0;
Packit df99a1
    int last_chunk=0;
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
      int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
      int chksize;
Packit df99a1
      for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
      {
Packit df99a1
        chunks++;
Packit df99a1
        if (chkid=="INCL")
Packit df99a1
        {
Packit df99a1
          G_TRY
Packit df99a1
          {
Packit df99a1
            process_incl_chunk(*iff.get_bytestream(), incl_cnt++);
Packit df99a1
          }
Packit df99a1
          G_CATCH(ex);
Packit df99a1
          {
Packit df99a1
            report_error(ex,(recover_errors <= SKIP_PAGES));
Packit df99a1
          }
Packit df99a1
          G_ENDCATCH;
Packit df99a1
        }else if(chkid=="FAKE")
Packit df99a1
        {
Packit df99a1
          set_needs_compression(true);
Packit df99a1
          set_can_compress(true);
Packit df99a1
        }else if(chkid=="BGjp")
Packit df99a1
        {
Packit df99a1
          set_can_compress(true);
Packit df99a1
        }else if(chkid=="Smmr")
Packit df99a1
        {
Packit df99a1
          set_can_compress(true);
Packit df99a1
        }
Packit df99a1
        iff.seek_close_chunk();
Packit df99a1
      }
Packit df99a1
      if (chunks_number < 0) chunks_number=last_chunk;
Packit df99a1
    }
Packit df99a1
    G_CATCH(ex)
Packit df99a1
    {	
Packit df99a1
      if (chunks_number < 0)
Packit df99a1
        chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
      report_error(ex,(recover_errors <= SKIP_PAGES));
Packit df99a1
    }
Packit df99a1
    G_ENDCATCH;
Packit df99a1
  }
Packit df99a1
  flags|=INCL_FILES_CREATED;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<JB2Dict>
Packit df99a1
DjVuFile::static_get_fgjd(void *arg)
Packit df99a1
{
Packit df99a1
  DjVuFile *file = (DjVuFile*)arg;
Packit df99a1
  return file->get_fgjd(1);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<JB2Dict>
Packit df99a1
DjVuFile::get_fgjd(int block)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  // Simplest case
Packit df99a1
  if (fgjd)
Packit df99a1
    return fgjd;
Packit df99a1
  // Check wether included files
Packit df99a1
  chunk_mon.enter();
Packit df99a1
  G_TRY {
Packit df99a1
    for(;;)
Packit df99a1
    {
Packit df99a1
      int active = 0;
Packit df99a1
      GPList<DjVuFile> incs = get_included_files();
Packit df99a1
      for (GPosition pos=incs.firstpos(); pos; ++pos)
Packit df99a1
      {
Packit df99a1
        GP<DjVuFile> file = incs[pos];
Packit df99a1
        if (file->is_decoding())
Packit df99a1
          active = 1;
Packit df99a1
        GP<JB2Dict> fgjd = file->get_fgjd();
Packit df99a1
        if (fgjd)
Packit df99a1
        {
Packit df99a1
          chunk_mon.leave();
Packit df99a1
          return fgjd;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      // Exit if non-blocking mode
Packit df99a1
      if (! block)
Packit df99a1
        break;
Packit df99a1
      // Exit if there is no decoding activity
Packit df99a1
      if (! active)
Packit df99a1
        break;
Packit df99a1
      // Wait until a new chunk gets decoded
Packit df99a1
      wait_for_chunk();
Packit df99a1
    }
Packit df99a1
  } G_CATCH_ALL {
Packit df99a1
    chunk_mon.leave();
Packit df99a1
    G_RETHROW;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
  chunk_mon.leave();
Packit df99a1
  if (is_decode_stopped()) G_THROW( DataPool::Stop );
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuFile::get_dpi(int w, int h)
Packit df99a1
{
Packit df99a1
  int dpi=0, red=1;
Packit df99a1
  if (info)
Packit df99a1
  {
Packit df99a1
    for(red=1; red<=12; red++)
Packit df99a1
      if ((info->width+red-1)/red==w)
Packit df99a1
        if ((info->height+red-1)/red==h)
Packit df99a1
          break;
Packit df99a1
    if (red>12)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.corrupt_BG44") );
Packit df99a1
    dpi=info->dpi;
Packit df99a1
  }
Packit df99a1
  return (dpi ? dpi : 300)/red;
Packit df99a1
}
Packit df99a1
Packit df99a1
static inline bool
Packit df99a1
is_info(const GUTF8String &chkid)
Packit df99a1
{
Packit df99a1
  return (chkid=="INFO");
Packit df99a1
}
Packit df99a1
Packit df99a1
static inline bool
Packit df99a1
is_annotation(const GUTF8String &chkid)
Packit df99a1
{
Packit df99a1
  return (chkid=="ANTa" ||
Packit df99a1
    chkid=="ANTz" ||
Packit df99a1
    chkid=="FORM:ANNO" ); 
Packit df99a1
}
Packit df99a1
Packit df99a1
static inline bool
Packit df99a1
is_text(const GUTF8String &chkid)
Packit df99a1
{
Packit df99a1
  return (chkid=="TXTa" || chkid=="TXTz");
Packit df99a1
}
Packit df99a1
Packit df99a1
static inline bool
Packit df99a1
is_meta(const GUTF8String &chkid)
Packit df99a1
{
Packit df99a1
  return (chkid=="METa" || chkid=="METz");
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuFile::decode_chunk( const GUTF8String &id, const GP<ByteStream> &gbs,
Packit df99a1
  bool djvi, bool djvu, bool iw44)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::decode_chunk()\n");
Packit df99a1
  ByteStream &bs=*gbs;
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  // If this object is referenced by only one GP<> pointer, this
Packit df99a1
  // pointer should be the "life_saver" created by the decoding thread.
Packit df99a1
  // If it is the only GP<> pointer, then nobody is interested in the
Packit df99a1
  // results of the decoding and we can abort now with #DataPool::Stop#
Packit df99a1
  if (get_count()==1)
Packit df99a1
    G_THROW( DataPool::Stop );
Packit df99a1
  
Packit df99a1
  GUTF8String desc = ERR_MSG("DjVuFile.unrecog_chunk");
Packit df99a1
  GUTF8String chkid = id;
Packit df99a1
  DEBUG_MSG("DjVuFile::decode_chunk() : decoding " << id << "\n");
Packit df99a1
  
Packit df99a1
  // INFO  (information chunk for djvu page)
Packit df99a1
  if (is_info(chkid) && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (info)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.corrupt_dupl") );
Packit df99a1
    if (djvi)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.corrupt_INFO") );
Packit df99a1
    // DjVuInfo::decode no longer throws version exceptions
Packit df99a1
    GP<DjVuInfo> xinfo=DjVuInfo::create();
Packit df99a1
    xinfo->decode(bs);
Packit df99a1
    info = xinfo;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.page_info") );
Packit df99a1
    // Consistency checks (previously in DjVuInfo::decode)
Packit df99a1
    if (info->width<0 || info->height<0)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.corrupt_zero") );
Packit df99a1
    if (info->version >= DJVUVERSION_TOO_NEW)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.new_version") "\t" 
Packit df99a1
               STRINGIFY(DJVUVERSION_TOO_NEW) );
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // INCL (inclusion chunk)
Packit df99a1
  else if (chkid == "INCL" && (djvi || djvu || iw44))
Packit df99a1
  {
Packit df99a1
    GP<DjVuFile> file=process_incl_chunk(bs);
Packit df99a1
    if (file)
Packit df99a1
    {
Packit df99a1
      int decode_was_already_started = 1;
Packit df99a1
      {
Packit df99a1
        GMonitorLock lock(&file->flags);
Packit df99a1
          // Start decoding
Packit df99a1
        if(file->resume_decode())
Packit df99a1
        {
Packit df99a1
          decode_was_already_started = 0;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      // Send file notifications if previously started
Packit df99a1
      if (decode_was_already_started)
Packit df99a1
      {
Packit df99a1
        // May send duplicate notifications...
Packit df99a1
        if (file->is_decode_ok())
Packit df99a1
          get_portcaster()->notify_file_flags_changed(file, DECODE_OK, 0);
Packit df99a1
        else if (file->is_decode_failed())
Packit df99a1
          get_portcaster()->notify_file_flags_changed(file, DECODE_FAILED, 0);
Packit df99a1
      }
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.indir_chunk1") "\t" + file->get_url().fname() );
Packit df99a1
    } else
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.indir_chunk2") );
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // Djbz (JB2 Dictionary)
Packit df99a1
  else if (chkid == "Djbz" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (this->fgjd)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_Dxxx") );
Packit df99a1
    if (this->fgjd)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.Dxxx_after_Sxxx") );
Packit df99a1
    GP<JB2Dict> fgjd = JB2Dict::create();
Packit df99a1
    fgjd->decode(gbs);
Packit df99a1
    this->fgjd = fgjd;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.shape_dict") "\t%d", fgjd->get_shape_count() );
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // Sjbz (JB2 encoded mask)
Packit df99a1
  else if (chkid=="Sjbz" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (this->fgjb)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
Packit df99a1
    GP<JB2Image> fgjb=JB2Image::create();
Packit df99a1
    // ---- begin hack
Packit df99a1
    if (info && info->version <=18)
Packit df99a1
      fgjb->reproduce_old_bug = true;
Packit df99a1
    // ---- end hack
Packit df99a1
    fgjb->decode(gbs, static_get_fgjd, (void*)this);
Packit df99a1
    this->fgjb = fgjb;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.fg_mask") "\t%d\t%d\t%d",
Packit df99a1
      fgjb->get_width(), fgjb->get_height(),
Packit df99a1
      get_dpi(fgjb->get_width(), fgjb->get_height()));
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // Smmr (MMR-G4 encoded mask)
Packit df99a1
  else if (chkid=="Smmr" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (this->fgjb)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
Packit df99a1
    set_can_compress(true);
Packit df99a1
    this->fgjb = MMRDecoder::decode(gbs);
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.G4_mask") "\t%d\t%d\t%d",
Packit df99a1
      fgjb->get_width(), fgjb->get_height(),
Packit df99a1
      get_dpi(fgjb->get_width(), fgjb->get_height()));
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // BG44 (background wavelets)
Packit df99a1
  else if (chkid == "BG44" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (!bg44)
Packit df99a1
    {
Packit df99a1
      if (bgpm)
Packit df99a1
        G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
Packit df99a1
      // First chunk
Packit df99a1
      GP<IW44Image> bg44=IW44Image::create_decode(IW44Image::COLOR);
Packit df99a1
      bg44->decode_chunk(gbs);
Packit df99a1
      this->bg44 = bg44;
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.IW44_bg1") "\t%d\t%d\t%d",
Packit df99a1
		      bg44->get_width(), bg44->get_height(),
Packit df99a1
          get_dpi(bg44->get_width(), bg44->get_height()));
Packit df99a1
    } 
Packit df99a1
    else
Packit df99a1
    {
Packit df99a1
      // Refinement chunks
Packit df99a1
      GP<IW44Image> bg44 = this->bg44;
Packit df99a1
      bg44->decode_chunk(gbs);
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.IW44_bg2") "\t%d\t%d",
Packit df99a1
		      bg44->get_serial(), get_dpi(bg44->get_width(), bg44->get_height()));
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // FG44 (foreground wavelets)
Packit df99a1
  else if (chkid == "FG44" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (fgpm || fgbc)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
Packit df99a1
    GP<IW44Image> gfg44=IW44Image::create_decode(IW44Image::COLOR);
Packit df99a1
    IW44Image &fg44=*gfg44;
Packit df99a1
    fg44.decode_chunk(gbs);
Packit df99a1
    fgpm=fg44.get_pixmap();
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.IW44_fg") "\t%d\t%d\t%d",
Packit df99a1
      fg44.get_width(), fg44.get_height(),
Packit df99a1
      get_dpi(fg44.get_width(), fg44.get_height()));
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // LINK (background LINK)
Packit df99a1
  else if (chkid == "LINK" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (bg44 || bgpm)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
Packit df99a1
    if(djvu_decode_codec)
Packit df99a1
    {
Packit df99a1
      set_modified(true);
Packit df99a1
      set_can_compress(true);
Packit df99a1
      set_needs_compression(true);
Packit df99a1
      this->bgpm = djvu_decode_codec(bs);
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.color_import1") "\t%d\t%d\t%d",
Packit df99a1
        bgpm->columns(), bgpm->rows(),
Packit df99a1
        get_dpi(bgpm->columns(), bgpm->rows()));
Packit df99a1
    }else
Packit df99a1
    {
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.color_import2") );
Packit df99a1
    }
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // BGjp (background JPEG)
Packit df99a1
  else if (chkid == "BGjp" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (bg44 || bgpm)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
Packit df99a1
    set_can_compress(true);
Packit df99a1
#ifdef NEED_JPEG_DECODER
Packit df99a1
    this->bgpm = JPEGDecoder::decode(bs);
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG_bg1") "\t%d\t%d\t%d",
Packit df99a1
      bgpm->columns(), bgpm->rows(),
Packit df99a1
      get_dpi(bgpm->columns(), bgpm->rows()));
Packit df99a1
#else
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG_bg2") );
Packit df99a1
#endif
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // FGjp (foreground JPEG)
Packit df99a1
  else if (chkid == "FGjp" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (fgpm || fgbc)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
Packit df99a1
#ifdef NEED_JPEG_DECODER
Packit df99a1
    this->fgpm = JPEGDecoder::decode(bs);
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG_fg1") "\t%d\t%d\t%d",
Packit df99a1
      fgpm->columns(), fgpm->rows(),
Packit df99a1
      get_dpi(fgpm->columns(), fgpm->rows()));
Packit df99a1
#else
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG_fg2") );
Packit df99a1
#endif
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // BG2k (background JPEG-2000) Note: JPEG2K bitstream not finalized.
Packit df99a1
  else if (chkid == "BG2k" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (bg44)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG2K_bg") );
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // FG2k (foreground JPEG-2000) Note: JPEG2K bitstream not finalized.
Packit df99a1
  else if (chkid == "FG2k" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (fgpm || fgbc)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JPEG2K_fg") );
Packit df99a1
  } 
Packit df99a1
  
Packit df99a1
  // FGbz (foreground color vector)
Packit df99a1
  else if (chkid == "FGbz" && (djvu || djvi))
Packit df99a1
  {
Packit df99a1
    if (fgpm || fgbc)
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
Packit df99a1
    GP<DjVuPalette> fgbc = DjVuPalette::create();
Packit df99a1
    fgbc->decode(gbs);
Packit df99a1
    this->fgbc = fgbc;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.JB2_fg") "\t%d\t%d",
Packit df99a1
      fgbc->size(), fgbc->colordata.size());
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // BM44/PM44 (IW44 data)
Packit df99a1
  else if ((chkid == "PM44" || chkid=="BM44") && iw44)
Packit df99a1
  {
Packit df99a1
    if (!bg44)
Packit df99a1
    {
Packit df99a1
      // First chunk
Packit df99a1
      GP<IW44Image> bg44 = IW44Image::create_decode(IW44Image::COLOR);
Packit df99a1
      bg44->decode_chunk(gbs);
Packit df99a1
      GP<DjVuInfo> info = DjVuInfo::create();
Packit df99a1
      info->width = bg44->get_width();
Packit df99a1
      info->height = bg44->get_height();
Packit df99a1
      info->dpi = 100;
Packit df99a1
      this->bg44 = bg44;
Packit df99a1
      this->info = info;
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.IW44_data1") "\t%d\t%d\t%d",
Packit df99a1
                   bg44->get_width(), bg44->get_height(),
Packit df99a1
                   get_dpi(bg44->get_width(), bg44->get_height()));
Packit df99a1
    } 
Packit df99a1
    else
Packit df99a1
    {
Packit df99a1
      // Refinement chunks
Packit df99a1
      GP<IW44Image> bg44 = this->bg44;
Packit df99a1
      bg44->decode_chunk(gbs);
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.IW44_data2") "\t%d\t%d",
Packit df99a1
                   bg44->get_serial(),
Packit df99a1
                   get_dpi(bg44->get_width(), bg44->get_height()));
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // NDIR (obsolete navigation chunk)
Packit df99a1
  else if (chkid == "NDIR")
Packit df99a1
  {
Packit df99a1
    GP<DjVuNavDir> dir=DjVuNavDir::create(url);
Packit df99a1
    dir->decode(bs);
Packit df99a1
    this->dir=dir;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.nav_dir") );
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // FORM:ANNO (obsolete) (must be before other annotations)
Packit df99a1
  else if (chkid == "FORM:ANNO") 
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> gachunk(ByteStream::create());
Packit df99a1
      ByteStream &achunk=*gachunk;
Packit df99a1
      achunk.copy(bs);
Packit df99a1
      achunk.seek(0);
Packit df99a1
      GCriticalSectionLock lock(&anno_lock);
Packit df99a1
      if (! anno)
Packit df99a1
      {
Packit df99a1
        anno=ByteStream::create();
Packit df99a1
      }
Packit df99a1
      anno->seek(0,SEEK_END);
Packit df99a1
      if (anno->tell())
Packit df99a1
      {
Packit df99a1
        anno->write((void*)"", 1);
Packit df99a1
      }
Packit df99a1
      // Copy data
Packit df99a1
      anno->copy(achunk);
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.anno1") );
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  // ANTa/ANTx/TXTa/TXTz annotations
Packit df99a1
  else if (is_annotation(chkid))  // but not FORM:ANNO
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> gachunk(ByteStream::create());
Packit df99a1
      ByteStream &achunk=*gachunk;
Packit df99a1
      achunk.copy(bs);
Packit df99a1
      achunk.seek(0);
Packit df99a1
      GCriticalSectionLock lock(&anno_lock);
Packit df99a1
      if (! anno)
Packit df99a1
      {
Packit df99a1
        anno = ByteStream::create();
Packit df99a1
      }
Packit df99a1
      anno->seek(0,SEEK_END);
Packit df99a1
      if (anno->tell() & 1)
Packit df99a1
      {
Packit df99a1
        anno->write((const void*)"", 1);
Packit df99a1
      }
Packit df99a1
      // Recreate chunk header
Packit df99a1
      const GP<IFFByteStream> giffout(IFFByteStream::create(anno));
Packit df99a1
      IFFByteStream &iffout=*giffout;
Packit df99a1
      iffout.put_chunk(id);
Packit df99a1
      iffout.copy(achunk);
Packit df99a1
      iffout.close_chunk();
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.anno2") );
Packit df99a1
    }
Packit df99a1
  else if (is_text(chkid))
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> gachunk(ByteStream::create());
Packit df99a1
      ByteStream &achunk=*gachunk;
Packit df99a1
      achunk.copy(bs);
Packit df99a1
      achunk.seek(0);
Packit df99a1
      GCriticalSectionLock lock(&text_lock);
Packit df99a1
      if (! text)
Packit df99a1
      {
Packit df99a1
        text = ByteStream::create();
Packit df99a1
      }
Packit df99a1
      text->seek(0,SEEK_END);
Packit df99a1
      if (text->tell())
Packit df99a1
      {
Packit df99a1
        text->write((const void*)"", 1);
Packit df99a1
      }
Packit df99a1
      // Recreate chunk header
Packit df99a1
      const GP<IFFByteStream> giffout(IFFByteStream::create(text));
Packit df99a1
      IFFByteStream &iffout=*giffout;
Packit df99a1
      iffout.put_chunk(id);
Packit df99a1
      iffout.copy(achunk);
Packit df99a1
      iffout.close_chunk();
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.text") );
Packit df99a1
    }
Packit df99a1
  else if (is_meta(chkid))
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> gachunk(ByteStream::create());
Packit df99a1
      ByteStream &achunk=*gachunk;
Packit df99a1
      achunk.copy(bs);
Packit df99a1
      achunk.seek(0);
Packit df99a1
      GCriticalSectionLock lock(&meta_lock);
Packit df99a1
      if (! meta)
Packit df99a1
      {
Packit df99a1
        meta = ByteStream::create();
Packit df99a1
      }
Packit df99a1
      meta->seek(0,SEEK_END);
Packit df99a1
      if (meta->tell())
Packit df99a1
      {
Packit df99a1
        meta->write((const void*)"", 1);
Packit df99a1
      }
Packit df99a1
      // Recreate chunk header
Packit df99a1
      const GP<IFFByteStream> giffout(IFFByteStream::create(meta));
Packit df99a1
      IFFByteStream &iffout=*giffout;
Packit df99a1
      iffout.put_chunk(id);
Packit df99a1
      iffout.copy(achunk);
Packit df99a1
      iffout.close_chunk();
Packit df99a1
    }
Packit df99a1
  else if (chkid == "CELX" || chkid == "SINF")
Packit df99a1
    {
Packit df99a1
      G_THROW( ERR_MSG("DjVuFile.securedjvu") );
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  // Return description
Packit df99a1
  return desc;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs))
Packit df99a1
{
Packit df99a1
  djvu_decode_codec=codec;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::decode(const GP<ByteStream> &gbs)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::decode(), url='" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
  
Packit df99a1
  // Get form chunk
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(gbs));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid)) 
Packit df99a1
    REPORT_EOF(true)
Packit df99a1
    
Packit df99a1
    // Check file format
Packit df99a1
  bool djvi = (chkid=="FORM:DJVI")?true:false;
Packit df99a1
  bool djvu = (chkid=="FORM:DJVU")?true:false;
Packit df99a1
  bool iw44 = ((chkid=="FORM:PM44") || (chkid=="FORM:BM44"));
Packit df99a1
  if (djvi || djvu)
Packit df99a1
    mimetype = "image/x.djvu";
Packit df99a1
  else if (iw44)
Packit df99a1
    mimetype = "image/x-iw44";
Packit df99a1
  else
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.unexp_image") );
Packit df99a1
  
Packit df99a1
  // Process chunks
Packit df99a1
  int size_so_far=iff.tell();
Packit df99a1
  int chunks=0;
Packit df99a1
  int last_chunk=0;
Packit df99a1
  G_TRY
Packit df99a1
  {
Packit df99a1
    int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
    int chksize;
Packit df99a1
    for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
    {
Packit df99a1
      chunks++;
Packit df99a1
Packit df99a1
      // Decode and get chunk description
Packit df99a1
      GUTF8String str = decode_chunk(chkid, iff.get_bytestream(), djvi, djvu, iw44);
Packit df99a1
      // Add parameters to the chunk description to give the size and chunk id
Packit df99a1
      GUTF8String desc;
Packit df99a1
      desc.format("\t%5.1f\t%s", chksize/1024.0, (const char*)chkid);
Packit df99a1
      // Append the whole thing to the growing file description
Packit df99a1
      description = description + str + desc + "\n";
Packit df99a1
Packit df99a1
      pcaster->notify_chunk_done(this, chkid);
Packit df99a1
      // Close chunk
Packit df99a1
      iff.seek_close_chunk();
Packit df99a1
      // Record file size
Packit df99a1
      size_so_far=iff.tell();
Packit df99a1
    }
Packit df99a1
    if (chunks_number < 0) chunks_number=last_chunk;
Packit df99a1
  }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
  {
Packit df99a1
    if(!ex.cmp_cause(ByteStream::EndOfFile))
Packit df99a1
    {
Packit df99a1
      if (chunks_number < 0)
Packit df99a1
        chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
      report_error(ex,(recover_errors <= SKIP_PAGES));
Packit df99a1
    }else
Packit df99a1
    {
Packit df99a1
      report_error(ex,true);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  
Packit df99a1
  // Record file size
Packit df99a1
  file_size=size_so_far;
Packit df99a1
  // Close form chunk
Packit df99a1
  iff.close_chunk();
Packit df99a1
  // Close BG44 codec
Packit df99a1
  if (bg44) 
Packit df99a1
    bg44->close_codec();
Packit df99a1
  
Packit df99a1
  // Complete description
Packit df99a1
  if (djvu && !info)
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.corrupt_missing_info") );
Packit df99a1
  if (iw44 && !info)
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.corrupt_missing_IW44") );
Packit df99a1
  if (info)
Packit df99a1
  {
Packit df99a1
    GUTF8String desc;
Packit df99a1
    if (djvu || djvi)
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.djvu_header") "\t%d\t%d\t%d\t%d", 
Packit df99a1
        info->width, info->height,
Packit df99a1
        info->dpi, info->version);
Packit df99a1
    else if (iw44)
Packit df99a1
      desc.format( ERR_MSG("DjVuFile.IW44_header") "\t%d\t%d\t%d", 
Packit df99a1
        info->width, info->height, info->dpi);
Packit df99a1
    description=desc + "\n" + description;
Packit df99a1
    int rawsize=info->width*info->height*3;
Packit df99a1
    desc.format( ERR_MSG("DjVuFile.ratio") "\t%0.1f\t%0.1f",
Packit df99a1
      (double)rawsize/file_size, file_size/1024.0 );
Packit df99a1
    description=description+desc;
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::start_decode(void)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::start_decode(), url='" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  GThread * thread_to_delete=0;
Packit df99a1
  flags.enter();
Packit df99a1
  G_TRY {
Packit df99a1
    if (!(flags & DONT_START_DECODE) && !is_decoding())
Packit df99a1
    {
Packit df99a1
      if (flags & DECODE_STOPPED) reset();
Packit df99a1
      flags&=~(DECODE_OK | DECODE_STOPPED | DECODE_FAILED);
Packit df99a1
      flags|=DECODING;
Packit df99a1
      
Packit df99a1
      // Don't delete the thread while you're owning the flags lock
Packit df99a1
      // Beware of deadlock!
Packit df99a1
      thread_to_delete=decode_thread; decode_thread=0;
Packit df99a1
      
Packit df99a1
      // We want to create it right here to be able to stop the
Packit df99a1
      // decoding thread even before its function is called (it starts)
Packit df99a1
      decode_data_pool=DataPool::create(data_pool);
Packit df99a1
      decode_life_saver=this;
Packit df99a1
      
Packit df99a1
      decode_thread=new GThread();
Packit df99a1
      decode_thread->create(static_decode_func, this);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  G_CATCH_ALL
Packit df99a1
  {
Packit df99a1
    flags&=~DECODING;
Packit df99a1
    flags|=DECODE_FAILED;
Packit df99a1
    flags.leave();
Packit df99a1
    get_portcaster()->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
Packit df99a1
    delete thread_to_delete;
Packit df99a1
    G_RETHROW;
Packit df99a1
  }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  flags.leave();
Packit df99a1
  delete thread_to_delete;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::resume_decode(const bool sync)
Packit df99a1
{
Packit df99a1
  bool retval=false;
Packit df99a1
  {
Packit df99a1
    GMonitorLock lock(&flags);
Packit df99a1
    if( !is_decoding() && !is_decode_ok() && !is_decode_failed() )
Packit df99a1
    {
Packit df99a1
      start_decode();
Packit df99a1
      retval=true;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  if(sync)
Packit df99a1
  {
Packit df99a1
    wait_for_finish();
Packit df99a1
  }
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::stop_decode(bool sync)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  DEBUG_MSG("DjVuFile::stop_decode(), url='" << url <<
Packit df99a1
    "', sync=" << (int) sync << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  G_TRY
Packit df99a1
  {
Packit df99a1
    flags|=DONT_START_DECODE;
Packit df99a1
    
Packit df99a1
    // Don't stop SYNCHRONOUSLY from the thread where the decoding is going!!!
Packit df99a1
    {
Packit df99a1
      // First - ask every included child to stop in async mode
Packit df99a1
      GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
      for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
        inc_files_list[pos]->stop_decode(0);
Packit df99a1
      
Packit df99a1
//      if (decode_data_pool) decode_data_pool->stop();
Packit df99a1
    }
Packit df99a1
    
Packit df99a1
    if (sync)
Packit df99a1
    {
Packit df99a1
      while(1)
Packit df99a1
      {
Packit df99a1
        GP<DjVuFile> file;
Packit df99a1
        {
Packit df99a1
          GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
          for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
          {
Packit df99a1
            GP<DjVuFile> & f=inc_files_list[pos];
Packit df99a1
            if (f->is_decoding())
Packit df99a1
            {
Packit df99a1
              file=f; break;
Packit df99a1
            }
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        if (!file) break;
Packit df99a1
        
Packit df99a1
        file->stop_decode(1);
Packit df99a1
      }
Packit df99a1
      
Packit df99a1
      wait_for_finish(1);	// Wait for self termination
Packit df99a1
      
Packit df99a1
      // Don't delete the thread here. Until GPBase::preserve() is
Packit df99a1
      // reimplemented somehow at the GThread level.
Packit df99a1
      // delete decode_thread; decode_thread=0;
Packit df99a1
    }
Packit df99a1
    flags&=~(DONT_START_DECODE);
Packit df99a1
  } G_CATCH_ALL {
Packit df99a1
    flags&=~(DONT_START_DECODE);
Packit df99a1
    G_RETHROW;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::stop(bool only_blocked)
Packit df99a1
// This is a one-way function. There is no way to undo the stop()
Packit df99a1
// command.
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::stop(): Stopping everything\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  flags|=only_blocked ? BLOCKED_STOPPED : STOPPED;
Packit df99a1
  if (data_pool) data_pool->stop(only_blocked);
Packit df99a1
  GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
  for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
    inc_files_list[pos]->stop(only_blocked);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuNavDir>
Packit df99a1
DjVuFile::find_ndir(GMap<GURL, void *> & map)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  DEBUG_MSG("DjVuFile::find_ndir(): looking for NDIR in '" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  if (dir) return dir;
Packit df99a1
  
Packit df99a1
  if (!map.contains(url))
Packit df99a1
  {
Packit df99a1
    map[url]=0;
Packit df99a1
    
Packit df99a1
    GPList<DjVuFile> list=get_included_files(false);
Packit df99a1
    for(GPosition pos=list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GP<DjVuNavDir> d=list[pos]->find_ndir(map);
Packit df99a1
      if (d) return d;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuNavDir>
Packit df99a1
DjVuFile::find_ndir(void)
Packit df99a1
{
Packit df99a1
  GMap<GURL, void *> map;
Packit df99a1
  return find_ndir(map);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuNavDir>
Packit df99a1
DjVuFile::decode_ndir(GMap<GURL, void *> & map)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  DEBUG_MSG("DjVuFile::decode_ndir(): decoding for NDIR in '" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  if (dir) return dir;
Packit df99a1
  
Packit df99a1
  if (!map.contains(url))
Packit df99a1
  {
Packit df99a1
    map[url]=0;
Packit df99a1
    
Packit df99a1
    const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
    
Packit df99a1
    GUTF8String chkid;
Packit df99a1
    const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
    IFFByteStream &iff=*giff;
Packit df99a1
    if (!iff.get_chunk(chkid)) 
Packit df99a1
      REPORT_EOF(true)
Packit df99a1
Packit df99a1
    int chunks=0;
Packit df99a1
    int last_chunk=0;
Packit df99a1
#ifndef SLOW_BUT_EXACT_DETECTION_OF_NDIR
Packit df99a1
    int found_incl=0;
Packit df99a1
#endif
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
      int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
      int chksize;
Packit df99a1
      for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
      {
Packit df99a1
        chunks++;
Packit df99a1
        if (chkid=="NDIR")
Packit df99a1
        {
Packit df99a1
          GP<DjVuNavDir> d=DjVuNavDir::create(url);
Packit df99a1
          d->decode(*iff.get_bytestream());
Packit df99a1
          dir=d;
Packit df99a1
          break;
Packit df99a1
        }
Packit df99a1
#ifndef SLOW_BUT_EXACT_DETECTION_OF_NDIR
Packit df99a1
        if (chkid=="INCL")
Packit df99a1
          found_incl = 1;
Packit df99a1
        if (chunks>2 && !found_incl && !data_pool->is_eof())
Packit df99a1
          return 0;
Packit df99a1
#endif
Packit df99a1
        iff.seek_close_chunk();
Packit df99a1
      }
Packit df99a1
      if ((!dir)&&(chunks_number < 0)) chunks_number=last_chunk;
Packit df99a1
    }
Packit df99a1
    G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
       if(!ex.cmp_cause(ByteStream::EndOfFile))
Packit df99a1
       {
Packit df99a1
          if (chunks_number < 0)
Packit df99a1
             chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
          report_error(ex,(recover_errors<=SKIP_PAGES));
Packit df99a1
       }else
Packit df99a1
       {
Packit df99a1
          report_error(ex,true);
Packit df99a1
       }
Packit df99a1
    }
Packit df99a1
    G_ENDCATCH;
Packit df99a1
    
Packit df99a1
    data_pool->clear_stream();
Packit df99a1
    if (dir) return dir;
Packit df99a1
    
Packit df99a1
    GPList<DjVuFile> list=get_included_files(false);
Packit df99a1
    for(GPosition pos=list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GP<DjVuNavDir> d=list[pos]->decode_ndir(map);
Packit df99a1
      if (d) return d;
Packit df99a1
    }
Packit df99a1
    data_pool->clear_stream();
Packit df99a1
  }
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuNavDir>
Packit df99a1
DjVuFile::decode_ndir(void)
Packit df99a1
{
Packit df99a1
  GMap<GURL, void *> map;
Packit df99a1
  return decode_ndir(map);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_merged_anno(const GP<DjVuFile> & file,
Packit df99a1
  const GP<ByteStream> &gstr_out, const GList<GURL> & ignore_list,
Packit df99a1
  int level, int & max_level, GMap<GURL, void *> & map)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_merged_anno()\n");
Packit df99a1
  GURL url=file->get_url();
Packit df99a1
  if (!map.contains(url))
Packit df99a1
  {
Packit df99a1
    ByteStream &str_out=*gstr_out;
Packit df99a1
    map[url]=0;
Packit df99a1
Packit df99a1
    // Do the included files first (To make sure that they have
Packit df99a1
    // less precedence)
Packit df99a1
    // Depending on if we have all data present, we will
Packit df99a1
    // either create all included files or will use only
Packit df99a1
    // those that have already been created
Packit df99a1
    GPList<DjVuFile> list=file->get_included_files(!file->is_data_present());
Packit df99a1
    for(GPosition pos=list;pos;++pos)
Packit df99a1
      get_merged_anno(list[pos], gstr_out, ignore_list, level+1, max_level, map);
Packit df99a1
Packit df99a1
    // Now process the DjVuFile's own annotations
Packit df99a1
    if (!ignore_list.contains(file->get_url()))
Packit df99a1
      {
Packit df99a1
	if (!file->is_data_present() ||
Packit df99a1
	    (file->is_modified() && file->anno))
Packit df99a1
	  {
Packit df99a1
	    // Process the decoded (?) anno
Packit df99a1
	    GCriticalSectionLock lock(&file->anno_lock);
Packit df99a1
	    if (file->anno && file->anno->size())
Packit df99a1
	      {
Packit df99a1
		if (str_out.tell())
Packit df99a1
		  {
Packit df99a1
		    str_out.write((void *) "", 1);
Packit df99a1
		  }
Packit df99a1
		file->anno->seek(0);
Packit df99a1
		str_out.copy(*file->anno);
Packit df99a1
	      }
Packit df99a1
	  } else if (file->is_data_present())
Packit df99a1
	  {
Packit df99a1
	    // Copy all annotations chunks, but do NOT modify
Packit df99a1
	    // this->anno (to avoid correlation with DjVuFile::decode())
Packit df99a1
	    const GP<ByteStream> str(file->data_pool->get_stream());
Packit df99a1
	    const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
	    IFFByteStream &iff=*giff;
Packit df99a1
	    GUTF8String chkid;
Packit df99a1
	    if (iff.get_chunk(chkid))
Packit df99a1
	      while(iff.get_chunk(chkid))
Packit df99a1
		{
Packit df99a1
		  if (chkid=="FORM:ANNO")
Packit df99a1
		    {
Packit df99a1
		      if (max_level
Packit df99a1
			max_level=level;
Packit df99a1
		      if (str_out.tell())
Packit df99a1
			{
Packit df99a1
			  str_out.write((void *) "", 1);
Packit df99a1
			}
Packit df99a1
		      str_out.copy(*iff.get_bytestream());
Packit df99a1
		    }
Packit df99a1
		  else if (is_annotation(chkid)) // but not FORM:ANNO
Packit df99a1
		    {
Packit df99a1
		      if (max_level
Packit df99a1
			max_level=level;
Packit df99a1
		      if (str_out.tell()&&chkid != "ANTz")
Packit df99a1
			{
Packit df99a1
			  str_out.write((void *) "", 1);
Packit df99a1
			}
Packit df99a1
		      const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
		      IFFByteStream &iff_out=*giff_out;
Packit df99a1
		      iff_out.put_chunk(chkid);
Packit df99a1
		      iff_out.copy(*iff.get_bytestream());
Packit df99a1
		      iff_out.close_chunk();
Packit df99a1
		    }
Packit df99a1
		  iff.close_chunk();
Packit df99a1
		}
Packit df99a1
	    file->data_pool->clear_stream();
Packit df99a1
	  }
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DjVuFile::get_merged_anno(const GList<GURL> & ignore_list,
Packit df99a1
                          int * max_level_ptr)
Packit df99a1
                          // Will do the same thing as get_merged_anno(int *), but will
Packit df99a1
                          // ignore DjVuFiles with URLs from the ignore_list
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_merged_anno()\n");
Packit df99a1
  GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
  GMap<GURL, void *> map;
Packit df99a1
  int max_level=0;
Packit df99a1
  get_merged_anno(this, gstr, ignore_list, 0, max_level, map);
Packit df99a1
  if (max_level_ptr)
Packit df99a1
    *max_level_ptr=max_level;
Packit df99a1
  ByteStream &str=*gstr;
Packit df99a1
  if (!str.tell()) 
Packit df99a1
  {
Packit df99a1
    gstr=0;
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    str.seek(0);
Packit df99a1
  }
Packit df99a1
  return gstr;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DjVuFile::get_merged_anno(int * max_level_ptr)
Packit df99a1
// Will go down the DjVuFile's hierarchy and decode all DjVuAnno even
Packit df99a1
// when the DjVuFile is not fully decoded yet. To avoid correlations
Packit df99a1
// with DjVuFile::decode(), we do not modify DjVuFile::anno data.
Packit df99a1
//
Packit df99a1
// Files deeper in the hierarchy have less influence on the
Packit df99a1
// results. It means, for example, that the if annotations are
Packit df99a1
// specified in the top level page file and in a shared file,
Packit df99a1
// the top level page file settings will take precedence.
Packit df99a1
//
Packit df99a1
// NOTE! This function guarantees correct results only if the
Packit df99a1
// DjVuFile has all data
Packit df99a1
{
Packit df99a1
  GList<GURL> ignore_list;
Packit df99a1
  return get_merged_anno(ignore_list, max_level_ptr);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// [LB->BCR] The following six functions get_anno, get_text, get_meta 
Packit df99a1
// contain the same code in triplicate!!!
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_anno(
Packit df99a1
  const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_anno()\n");
Packit df99a1
  ByteStream &str_out=*gstr_out;
Packit df99a1
  if (!file->is_data_present() ||
Packit df99a1
      (file->is_modified() && file->anno))
Packit df99a1
    {
Packit df99a1
      // Process the decoded (?) anno
Packit df99a1
      GCriticalSectionLock lock(&file->anno_lock);
Packit df99a1
      if (file->anno && file->anno->size())
Packit df99a1
	{
Packit df99a1
	  if (str_out.tell())
Packit df99a1
	    {
Packit df99a1
	      str_out.write((void *) "", 1);
Packit df99a1
	    }
Packit df99a1
	  file->anno->seek(0);
Packit df99a1
	  str_out.copy(*file->anno);
Packit df99a1
	}
Packit df99a1
    } else if (file->is_data_present())
Packit df99a1
    {
Packit df99a1
      // Copy all anno chunks, but do NOT modify
Packit df99a1
      // DjVuFile::anno (to avoid correlation with DjVuFile::decode())
Packit df99a1
      const GP<ByteStream> str=file->data_pool->get_stream();
Packit df99a1
      const GP<IFFByteStream> giff=IFFByteStream::create(str);
Packit df99a1
      IFFByteStream &iff=*giff;
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      if (iff.get_chunk(chkid))
Packit df99a1
	{
Packit df99a1
	  while(iff.get_chunk(chkid))
Packit df99a1
	    {
Packit df99a1
	      if (is_annotation(chkid))
Packit df99a1
		{
Packit df99a1
		  if (str_out.tell())
Packit df99a1
		    {
Packit df99a1
		      str_out.write((void *) "", 1);
Packit df99a1
		    }
Packit df99a1
		  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
		  IFFByteStream &iff_out=*giff_out;
Packit df99a1
		  iff_out.put_chunk(chkid);
Packit df99a1
		  iff_out.copy(*iff.get_bytestream());
Packit df99a1
		  iff_out.close_chunk();
Packit df99a1
		}
Packit df99a1
	      iff.close_chunk();
Packit df99a1
	    }
Packit df99a1
	}
Packit df99a1
      file->data_pool->clear_stream();
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_text(
Packit df99a1
  const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_text()\n");
Packit df99a1
  ByteStream &str_out=*gstr_out;
Packit df99a1
  if (!file->is_data_present() ||
Packit df99a1
      (file->is_modified() && file->text))
Packit df99a1
    {
Packit df99a1
      // Process the decoded (?) text
Packit df99a1
      GCriticalSectionLock lock(&file->text_lock);
Packit df99a1
      if (file->text && file->text->size())
Packit df99a1
	{
Packit df99a1
	  if (str_out.tell())
Packit df99a1
	    {
Packit df99a1
	      str_out.write((void *) "", 1);
Packit df99a1
	    }
Packit df99a1
	  file->text->seek(0);
Packit df99a1
	  str_out.copy(*file->text);
Packit df99a1
	}
Packit df99a1
    } else if (file->is_data_present())
Packit df99a1
    {
Packit df99a1
      // Copy all text chunks, but do NOT modify
Packit df99a1
      // DjVuFile::text (to avoid correlation with DjVuFile::decode())
Packit df99a1
      const GP<ByteStream> str=file->data_pool->get_stream();
Packit df99a1
      const GP<IFFByteStream> giff=IFFByteStream::create(str);
Packit df99a1
      IFFByteStream &iff=*giff;
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      if (iff.get_chunk(chkid))
Packit df99a1
	{
Packit df99a1
	  while(iff.get_chunk(chkid))
Packit df99a1
	    {
Packit df99a1
	      if (is_text(chkid))
Packit df99a1
		{
Packit df99a1
		  if (str_out.tell())
Packit df99a1
		    {
Packit df99a1
		      str_out.write((void *) "", 1);
Packit df99a1
		    }
Packit df99a1
		  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
		  IFFByteStream &iff_out=*giff_out;
Packit df99a1
		  iff_out.put_chunk(chkid);
Packit df99a1
		  iff_out.copy(*iff.get_bytestream());
Packit df99a1
		  iff_out.close_chunk();
Packit df99a1
		}
Packit df99a1
	      iff.close_chunk();
Packit df99a1
	    }
Packit df99a1
	}
Packit df99a1
      file->data_pool->clear_stream();
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_meta(
Packit df99a1
  const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_meta()\n");
Packit df99a1
  ByteStream &str_out=*gstr_out;
Packit df99a1
  if (!file->is_data_present() ||
Packit df99a1
      (file->is_modified() && file->meta))
Packit df99a1
    {
Packit df99a1
      // Process the decoded (?) meta
Packit df99a1
      GCriticalSectionLock lock(&file->meta_lock);
Packit df99a1
      if (file->meta && file->meta->size())
Packit df99a1
	{
Packit df99a1
	  if (str_out.tell())
Packit df99a1
	    {
Packit df99a1
	      str_out.write((void *) "", 1);
Packit df99a1
	    }
Packit df99a1
	  file->meta->seek(0);
Packit df99a1
	  str_out.copy(*file->meta);
Packit df99a1
	}
Packit df99a1
    } else if (file->is_data_present())
Packit df99a1
    {
Packit df99a1
      // Copy all meta chunks, but do NOT modify
Packit df99a1
      // DjVuFile::meta (to avoid correlation with DjVuFile::decode())
Packit df99a1
      const GP<ByteStream> str=file->data_pool->get_stream();
Packit df99a1
      const GP<IFFByteStream> giff=IFFByteStream::create(str);
Packit df99a1
      IFFByteStream &iff=*giff;
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      if (iff.get_chunk(chkid))
Packit df99a1
	{
Packit df99a1
	  while(iff.get_chunk(chkid))
Packit df99a1
	    {
Packit df99a1
	      if (is_meta(chkid))
Packit df99a1
		{
Packit df99a1
		  if (str_out.tell())
Packit df99a1
		    {
Packit df99a1
		      str_out.write((void *) "", 1);
Packit df99a1
		    }
Packit df99a1
		  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
		  IFFByteStream &iff_out=*giff_out;
Packit df99a1
		  iff_out.put_chunk(chkid);
Packit df99a1
		  iff_out.copy(*iff.get_bytestream());
Packit df99a1
		  iff_out.close_chunk();
Packit df99a1
		}
Packit df99a1
	      iff.close_chunk();
Packit df99a1
	    }
Packit df99a1
	}
Packit df99a1
      file->data_pool->clear_stream();
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DjVuFile::get_anno(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_text(void)\n");
Packit df99a1
  GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
  get_anno(this, gstr);
Packit df99a1
  ByteStream &str=*gstr;
Packit df99a1
  if (!str.tell())
Packit df99a1
  { 
Packit df99a1
    gstr=0;
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    str.seek(0);
Packit df99a1
  }
Packit df99a1
  return gstr;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DjVuFile::get_text(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_text(void)\n");
Packit df99a1
  GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
  get_text(this, gstr);
Packit df99a1
  ByteStream &str=*gstr;
Packit df99a1
  if (!str.tell())
Packit df99a1
  { 
Packit df99a1
    gstr=0;
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    str.seek(0);
Packit df99a1
  }
Packit df99a1
  return gstr;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DjVuFile::get_meta(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::get_meta(void)\n");
Packit df99a1
  GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
  get_meta(this, gstr);
Packit df99a1
  ByteStream &str=*gstr;
Packit df99a1
  if (!str.tell())
Packit df99a1
  { 
Packit df99a1
    gstr=0;
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    str.seek(0);
Packit df99a1
  }
Packit df99a1
  return gstr;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::static_trigger_cb(void * cl_data)
Packit df99a1
{
Packit df99a1
  DjVuFile * th=(DjVuFile *) cl_data;
Packit df99a1
  G_TRY {
Packit df99a1
    GP<DjVuPort> port=DjVuPort::get_portcaster()->is_port_alive(th);
Packit df99a1
    if (port && port->inherits("DjVuFile"))
Packit df99a1
      ((DjVuFile *) (DjVuPort *) port)->trigger_cb();
Packit df99a1
  } G_CATCH(exc) {
Packit df99a1
    G_TRY {
Packit df99a1
      get_portcaster()->notify_error(th, exc.get_cause());
Packit df99a1
    } G_CATCH_ALL {} G_ENDCATCH;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::trigger_cb(void)
Packit df99a1
{
Packit df99a1
  GP<DjVuFile> life_saver=this;
Packit df99a1
  
Packit df99a1
  DEBUG_MSG("DjVuFile::trigger_cb(): got data for '" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  file_size=data_pool->get_length();
Packit df99a1
  flags|=DATA_PRESENT;
Packit df99a1
  get_portcaster()->notify_file_flags_changed(this, DATA_PRESENT, 0);
Packit df99a1
  
Packit df99a1
  if (!are_incl_files_created())
Packit df99a1
    process_incl_chunks();
Packit df99a1
  
Packit df99a1
  bool all=true;
Packit df99a1
  inc_files_lock.lock();
Packit df99a1
  GPList<DjVuFile> files_list=inc_files_list;
Packit df99a1
  inc_files_lock.unlock();
Packit df99a1
  for(GPosition pos=files_list;pos&&(all=files_list[pos]->is_all_data_present());++pos)
Packit df99a1
    EMPTY_LOOP;
Packit df99a1
  if (all)
Packit df99a1
  {
Packit df99a1
    DEBUG_MSG("DjVuFile::trigger_cb(): We have ALL data for '" << url << "'\n");
Packit df99a1
    flags|=ALL_DATA_PRESENT;
Packit df99a1
    get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::progress_cb(int pos, void * cl_data)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::progress_cb() called\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  DjVuFile * th=(DjVuFile *) cl_data;
Packit df99a1
  
Packit df99a1
  int length=th->decode_data_pool->get_length();
Packit df99a1
  if (length>0)
Packit df99a1
  {
Packit df99a1
    float progress=(float) pos/length;
Packit df99a1
    DEBUG_MSG("progress=" << progress << "\n");
Packit df99a1
    get_portcaster()->notify_decode_progress(th, progress);
Packit df99a1
  } else
Packit df99a1
  {
Packit df99a1
    DEBUG_MSG("DataPool size is still unknown => ignoring\n");
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
//*****************************************************************************
Packit df99a1
//******************************** Utilities **********************************
Packit df99a1
//*****************************************************************************
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::move(GMap<GURL, void *> & map, const GURL & dir_url)
Packit df99a1
// This function may block for data.
Packit df99a1
{
Packit df99a1
  if (!map.contains(url))
Packit df99a1
  {
Packit df99a1
    map[url]=0;
Packit df99a1
    
Packit df99a1
    url=GURL::UTF8(url.name(),dir_url);
Packit df99a1
    
Packit df99a1
    
Packit df99a1
    // Leave the lock here!
Packit df99a1
    GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
    for(GPosition pos=inc_files_list;pos;++pos)
Packit df99a1
      inc_files_list[pos]->move(map, dir_url);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::move(const GURL & dir_url)
Packit df99a1
// This function may block for data.
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::move(): dir_url='" << dir_url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  GMap<GURL, void *> map;
Packit df99a1
  move(map, dir_url);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::set_name(const GUTF8String &name)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::set_name(): name='" << name << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  url=GURL::UTF8(name,url.base());
Packit df99a1
}
Packit df99a1
Packit df99a1
//*****************************************************************************
Packit df99a1
//****************************** Data routines ********************************
Packit df99a1
//*****************************************************************************
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuFile::get_chunks_number(void)
Packit df99a1
{
Packit df99a1
  if(chunks_number < 0)
Packit df99a1
  {
Packit df99a1
    const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
    GUTF8String chkid;
Packit df99a1
    const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
    IFFByteStream &iff=*giff;
Packit df99a1
    if (!iff.get_chunk(chkid))
Packit df99a1
      REPORT_EOF(true)
Packit df99a1
      
Packit df99a1
      int chunks=0;
Packit df99a1
    int last_chunk=0;
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
      int chksize;
Packit df99a1
      for(;(chksize=iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
      {
Packit df99a1
        chunks++;
Packit df99a1
        iff.seek_close_chunk();
Packit df99a1
      }
Packit df99a1
      chunks_number=last_chunk;
Packit df99a1
    }
Packit df99a1
    G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
      report_error(ex,(recover_errors<=SKIP_PAGES));
Packit df99a1
    }
Packit df99a1
    G_ENDCATCH;
Packit df99a1
    data_pool->clear_stream();
Packit df99a1
  }
Packit df99a1
  return chunks_number;
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuFile::get_chunk_name(int chunk_num)
Packit df99a1
{
Packit df99a1
  if(chunk_num < 0)
Packit df99a1
  {
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.illegal_chunk") );
Packit df99a1
  }
Packit df99a1
  if((chunks_number >= 0)&&(chunk_num > chunks_number))
Packit df99a1
  {
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
Packit df99a1
  }
Packit df99a1
  check();
Packit df99a1
  
Packit df99a1
  GUTF8String name;
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid)) 
Packit df99a1
    REPORT_EOF(true)
Packit df99a1
    
Packit df99a1
    int chunks=0;
Packit df99a1
  int last_chunk=0;
Packit df99a1
  G_TRY
Packit df99a1
  {
Packit df99a1
    int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
    int chksize;
Packit df99a1
    for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
    {
Packit df99a1
      if (chunks++==chunk_num) { name=chkid; break; }
Packit df99a1
      iff.seek_close_chunk();
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
  {
Packit df99a1
    if (chunks_number < 0)
Packit df99a1
      chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
    report_error(ex,(recover_errors <= SKIP_PAGES));
Packit df99a1
  }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  if (!name.length())
Packit df99a1
  {
Packit df99a1
    if (chunks_number < 0) chunks_number=chunks;
Packit df99a1
    G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
Packit df99a1
  }
Packit df99a1
  return name;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::contains_chunk(const GUTF8String &chunk_name)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuFile::contains_chunk(): url='" << url << "', chunk_name='" <<
Packit df99a1
    chunk_name << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  bool contains=0;
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid)) 
Packit df99a1
    REPORT_EOF((recover_errors<=SKIP_PAGES))
Packit df99a1
    
Packit df99a1
    int chunks=0;
Packit df99a1
  int last_chunk=0;
Packit df99a1
  G_TRY
Packit df99a1
  {
Packit df99a1
    int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
    int chksize;
Packit df99a1
    for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
    {
Packit df99a1
      chunks++;
Packit df99a1
      if (chkid==chunk_name) { contains=1; break; }
Packit df99a1
      iff.seek_close_chunk();
Packit df99a1
    }
Packit df99a1
    if (!contains &&(chunks_number < 0)) chunks_number=last_chunk;
Packit df99a1
  }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
  {
Packit df99a1
    if (chunks_number < 0)
Packit df99a1
      chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
    report_error(ex,(recover_errors <= SKIP_PAGES));
Packit df99a1
  }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
  return contains;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::contains_anno(void)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  while(iff.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (is_annotation(chkid))
Packit df99a1
      return true;
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::contains_text(void)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  while(iff.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (is_text(chkid))
Packit df99a1
      return true;
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuFile::contains_meta(void)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  while(iff.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (is_meta(chkid))
Packit df99a1
      return true;
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
//*****************************************************************************
Packit df99a1
//****************************** Save routines ********************************
Packit df99a1
//*****************************************************************************
Packit df99a1
Packit df99a1
static void
Packit df99a1
copy_chunks(const GP<ByteStream> &from, IFFByteStream &ostr)
Packit df99a1
{
Packit df99a1
  from->seek(0);
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(from));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  int chksize;
Packit df99a1
  while ((chksize=iff.get_chunk(chkid)))
Packit df99a1
  {
Packit df99a1
    ostr.put_chunk(chkid);
Packit df99a1
    int ochksize=ostr.copy(*iff.get_bytestream());
Packit df99a1
    ostr.close_chunk();
Packit df99a1
    iff.seek_close_chunk();
Packit df99a1
    if(ochksize != chksize)
Packit df99a1
    {
Packit df99a1
      G_THROW( ByteStream::EndOfFile );
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::add_djvu_data(IFFByteStream & ostr, GMap<GURL, void *> & map,
Packit df99a1
                        const bool included_too, const bool no_ndir)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  if (map.contains(url)) return;
Packit df99a1
  bool top_level = !map.size();
Packit df99a1
  map[url]=0;
Packit df99a1
  bool processed_annotation = false;
Packit df99a1
  bool processed_text = false;
Packit df99a1
  bool processed_meta = false;
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> str(data_pool->get_stream());
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff(IFFByteStream::create(str));
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  if (!iff.get_chunk(chkid)) 
Packit df99a1
    REPORT_EOF(true)
Packit df99a1
    
Packit df99a1
    // Open toplevel form
Packit df99a1
    if (top_level) 
Packit df99a1
      ostr.put_chunk(chkid);
Packit df99a1
    // Process chunks
Packit df99a1
    int chunks=0;
Packit df99a1
    int last_chunk=0;
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
      int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
Packit df99a1
      int chksize;
Packit df99a1
      for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
Packit df99a1
      {
Packit df99a1
        chunks++;
Packit df99a1
        if (is_info(chkid) && info)
Packit df99a1
        {
Packit df99a1
          ostr.put_chunk(chkid);
Packit df99a1
          info->encode(*ostr.get_bytestream());
Packit df99a1
          ostr.close_chunk();
Packit df99a1
        }
Packit df99a1
        else if (chkid=="INCL" && included_too)
Packit df99a1
        {
Packit df99a1
          GP<DjVuFile> file = process_incl_chunk(*iff.get_bytestream());
Packit df99a1
          if (file)
Packit df99a1
          {
Packit df99a1
            if(recover_errors!=ABORT)
Packit df99a1
              file->set_recover_errors(recover_errors);
Packit df99a1
            if(verbose_eof)
Packit df99a1
              file->set_verbose_eof(verbose_eof);
Packit df99a1
            file->add_djvu_data(ostr, map, included_too, no_ndir);
Packit df99a1
          }
Packit df99a1
        } 
Packit df99a1
        else if (is_annotation(chkid) && anno && anno->size())
Packit df99a1
        {
Packit df99a1
          if (!processed_annotation)
Packit df99a1
          {
Packit df99a1
            processed_annotation = true;
Packit df99a1
            GCriticalSectionLock lock(&anno_lock);
Packit df99a1
            copy_chunks(anno, ostr);
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        else if (is_text(chkid) && text && text->size())
Packit df99a1
        {
Packit df99a1
          if (!processed_text)
Packit df99a1
          {
Packit df99a1
            processed_text = true;
Packit df99a1
            GCriticalSectionLock lock(&text_lock);
Packit df99a1
            copy_chunks(text, ostr);
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        else if (is_meta(chkid) && meta && meta->size())
Packit df99a1
        {
Packit df99a1
          if (!processed_meta)
Packit df99a1
          {
Packit df99a1
            processed_meta = true;
Packit df99a1
            GCriticalSectionLock lock(&meta_lock);
Packit df99a1
            copy_chunks(meta, ostr);
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        else if (chkid!="NDIR"||!(no_ndir || dir))
Packit df99a1
        {  // Copy NDIR chunks, but never generate new ones.
Packit df99a1
          ostr.put_chunk(chkid);
Packit df99a1
          ostr.copy(*iff.get_bytestream());
Packit df99a1
          ostr.close_chunk();
Packit df99a1
        }
Packit df99a1
        iff.seek_close_chunk();
Packit df99a1
      }
Packit df99a1
      if (chunks_number < 0) chunks_number=last_chunk;
Packit df99a1
    }
Packit df99a1
    G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      if(!ex.cmp_cause(ByteStream::EndOfFile))
Packit df99a1
      {
Packit df99a1
        if (chunks_number < 0)
Packit df99a1
          chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
Packit df99a1
        report_error(ex,(recover_errors<=SKIP_PAGES));
Packit df99a1
      }else
Packit df99a1
      {
Packit df99a1
        report_error(ex,true);
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
    G_ENDCATCH;
Packit df99a1
    
Packit df99a1
    // Otherwise, writes annotation at the end (annotations could be big)
Packit df99a1
    if (!processed_annotation && anno && anno->size())
Packit df99a1
    {
Packit df99a1
      processed_annotation = true;
Packit df99a1
      GCriticalSectionLock lock(&anno_lock);
Packit df99a1
      copy_chunks(anno, ostr);
Packit df99a1
    }
Packit df99a1
    if (!processed_text && text && text->size())
Packit df99a1
    {
Packit df99a1
      processed_text = true;
Packit df99a1
      GCriticalSectionLock lock(&text_lock);
Packit df99a1
      copy_chunks(text, ostr);
Packit df99a1
    }
Packit df99a1
    if (!processed_meta && meta && meta->size())
Packit df99a1
    {
Packit df99a1
      processed_meta = true;
Packit df99a1
      GCriticalSectionLock lock(&meta_lock);
Packit df99a1
      copy_chunks(meta, ostr);
Packit df99a1
    }
Packit df99a1
    // Close iff
Packit df99a1
    if (top_level) 
Packit df99a1
      ostr.close_chunk();
Packit df99a1
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<ByteStream>  
Packit df99a1
DjVuFile::get_djvu_bytestream(const bool included_too, const bool no_ndir)
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuFile::get_djvu_bytestream(): creating DjVu raw file\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   const GP<ByteStream> pbs(ByteStream::create());
Packit df99a1
   const GP<IFFByteStream> giff=IFFByteStream::create(pbs);
Packit df99a1
   IFFByteStream &iff=*giff;
Packit df99a1
   GMap<GURL, void *> map;
Packit df99a1
   add_djvu_data(iff, map, included_too, no_ndir);
Packit df99a1
   iff.flush();
Packit df99a1
   pbs->seek(0, SEEK_SET);
Packit df99a1
   return pbs;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuFile::get_djvu_data(const bool included_too, const bool no_ndir)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> pbs = get_djvu_bytestream(included_too, no_ndir);
Packit df99a1
  return DataPool::create(pbs);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::merge_anno(ByteStream &out)
Packit df99a1
{
Packit df99a1
  // Reuse get_merged_anno(), which is better than the previous
Packit df99a1
  // implementation due to three things:
Packit df99a1
  //  1. It works even before the file is completely decoded
Packit df99a1
  //  2. It merges annotations taking into account where a child DjVuFile
Packit df99a1
  //     is included.
Packit df99a1
  //  3. It handles loops in DjVuFile's hierarchy
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> str(get_merged_anno());
Packit df99a1
  if (str)
Packit df99a1
  {
Packit df99a1
    str->seek(0);
Packit df99a1
    if (out.tell())
Packit df99a1
    {
Packit df99a1
      out.write((void *) "", 1);
Packit df99a1
    }
Packit df99a1
    out.copy(*str);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_text(ByteStream &out)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> str(get_text());
Packit df99a1
  if (str)
Packit df99a1
  {
Packit df99a1
    str->seek(0);
Packit df99a1
    if (out.tell())
Packit df99a1
    {
Packit df99a1
      out.write((void *) "", 1);
Packit df99a1
    }
Packit df99a1
    out.copy(*str);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::get_meta(ByteStream &out)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> str(get_meta());
Packit df99a1
  if (str)
Packit df99a1
  {
Packit df99a1
    str->seek(0);
Packit df99a1
    if (out.tell())
Packit df99a1
    {
Packit df99a1
      out.write((void *) "", 1);
Packit df99a1
    }
Packit df99a1
    out.copy(*str);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//******************************* Modifying **********************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::remove_anno(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::remove_anno()\n");
Packit df99a1
  const GP<ByteStream> str_in(data_pool->get_stream());
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  if (!iff_in.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  iff_out.put_chunk(chkid);
Packit df99a1
  
Packit df99a1
  while(iff_in.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (!is_annotation(chkid))
Packit df99a1
    {
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      iff_out.copy(*iff_in.get_bytestream());
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_in.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  iff_out.close_chunk();
Packit df99a1
  
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data_pool=DataPool::create(gstr_out);
Packit df99a1
  chunks_number=-1;
Packit df99a1
  
Packit df99a1
  anno=0;
Packit df99a1
  
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::remove_text(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::remove_text()\n");
Packit df99a1
  const GP<ByteStream> str_in(data_pool->get_stream());
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  if (!iff_in.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  iff_out.put_chunk(chkid);
Packit df99a1
  
Packit df99a1
  while(iff_in.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (!is_text(chkid))
Packit df99a1
    {
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      iff_out.copy(*iff_in.get_bytestream());
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_in.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  iff_out.close_chunk();
Packit df99a1
  
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data_pool=DataPool::create(gstr_out);
Packit df99a1
  chunks_number=-1;
Packit df99a1
  
Packit df99a1
  text=0;
Packit df99a1
  
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::remove_meta(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::remove_meta()\n");
Packit df99a1
  const GP<ByteStream> str_in(data_pool->get_stream());
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  if (!iff_in.get_chunk(chkid))
Packit df99a1
    G_THROW( ByteStream::EndOfFile );
Packit df99a1
  
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  iff_out.put_chunk(chkid);
Packit df99a1
  
Packit df99a1
  while(iff_in.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    if (!is_meta(chkid))
Packit df99a1
    {
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      iff_out.copy(*iff_in.get_bytestream());
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_in.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  iff_out.close_chunk();
Packit df99a1
  
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data_pool=DataPool::create(gstr_out);
Packit df99a1
  chunks_number=-1;
Packit df99a1
  
Packit df99a1
  meta=0;
Packit df99a1
  
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::rebuild_data_pool(void)
Packit df99a1
{
Packit df99a1
  data_pool=get_djvu_data(false,false);
Packit df99a1
  chunks_number=1;
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
}
Packit df99a1
Packit df99a1
// Do NOT comment this function out. It's used by DjVuDocEditor to convert
Packit df99a1
// old-style DjVu documents to BUNDLED format.
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuFile::unlink_file(const GP<DataPool> & data, const GUTF8String &name)
Packit df99a1
// Will process contents of data[] and remove any INCL chunk
Packit df99a1
// containing 'name'
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::unlink_file()\n");
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> str_in(data->get_stream());
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  
Packit df99a1
  int chksize;
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  if (!iff_in.get_chunk(chkid)) return data;
Packit df99a1
  
Packit df99a1
  iff_out.put_chunk(chkid);
Packit df99a1
  
Packit df99a1
  while((chksize=iff_in.get_chunk(chkid)))
Packit df99a1
  {
Packit df99a1
    if (chkid=="INCL")
Packit df99a1
    {
Packit df99a1
      GUTF8String incl_str;
Packit df99a1
      char buffer[1024];
Packit df99a1
      int length;
Packit df99a1
      while((length=iff_in.read(buffer, 1024)))
Packit df99a1
        incl_str+=GUTF8String(buffer, length);
Packit df99a1
      
Packit df99a1
      // Eat '\n' in the beginning and at the end
Packit df99a1
      while(incl_str.length() && incl_str[0]=='\n')
Packit df99a1
      {
Packit df99a1
        incl_str=incl_str.substr(1,(unsigned int)(-1));
Packit df99a1
      }
Packit df99a1
      while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
Packit df99a1
      {
Packit df99a1
        incl_str.setat(incl_str.length()-1, 0);
Packit df99a1
      }
Packit df99a1
      if (incl_str!=name)
Packit df99a1
      {
Packit df99a1
        iff_out.put_chunk(chkid);
Packit df99a1
        iff_out.get_bytestream()->writestring(incl_str);
Packit df99a1
        iff_out.close_chunk();
Packit df99a1
      }
Packit df99a1
    } else
Packit df99a1
    {
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      char buffer[1024];
Packit df99a1
      int length;
Packit df99a1
      for(const GP<ByteStream> gbs(iff_out.get_bytestream());
Packit df99a1
        (length=iff_in.read(buffer, 1024));)
Packit df99a1
      {
Packit df99a1
        gbs->writall(buffer, length);
Packit df99a1
      }
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_in.close_chunk();
Packit df99a1
  }
Packit df99a1
  iff_out.close_chunk();
Packit df99a1
  iff_out.flush();
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data->clear_stream();
Packit df99a1
  return DataPool::create(gstr_out);
Packit df99a1
}
Packit df99a1
Packit df99a1
#ifndef NEED_DECODER_ONLY
Packit df99a1
void
Packit df99a1
DjVuFile::insert_file(const GUTF8String &id, int chunk_num)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "', chunk_num="
Packit df99a1
    << chunk_num << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  // First: create new data
Packit df99a1
  const GP<ByteStream> str_in(data_pool->get_stream());
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  
Packit df99a1
  int chunk_cnt=0;
Packit df99a1
  bool done=false;
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  if (iff_in.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    iff_out.put_chunk(chkid);
Packit df99a1
    int chksize;
Packit df99a1
    while((chksize=iff_in.get_chunk(chkid)))
Packit df99a1
    {
Packit df99a1
      if (chunk_cnt++==chunk_num)
Packit df99a1
      {
Packit df99a1
        iff_out.put_chunk("INCL");
Packit df99a1
        iff_out.get_bytestream()->writestring(id);
Packit df99a1
        iff_out.close_chunk();
Packit df99a1
        done=true;
Packit df99a1
      }
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      iff_out.copy(*iff_in.get_bytestream());
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
      iff_in.close_chunk();
Packit df99a1
    }
Packit df99a1
    if (!done)
Packit df99a1
    {
Packit df99a1
      iff_out.put_chunk("INCL");
Packit df99a1
      iff_out.get_bytestream()->writestring(id);
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_out.close_chunk();
Packit df99a1
  }
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data_pool=DataPool::create(gstr_out);
Packit df99a1
  chunks_number=-1;
Packit df99a1
  
Packit df99a1
  // Second: create missing DjVuFiles
Packit df99a1
  process_incl_chunks();
Packit df99a1
  
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
  data_pool->clear_stream();
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::unlink_file(const GUTF8String &id)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  // Remove the file from the list of included files
Packit df99a1
  {
Packit df99a1
    GURL url=DjVuPort::get_portcaster()->id_to_url(this, id);
Packit df99a1
    if (url.is_empty()) url=GURL::UTF8(id,this->url.base());
Packit df99a1
    GCriticalSectionLock lock(&inc_files_lock);
Packit df99a1
    for(GPosition pos=inc_files_list;pos;)
Packit df99a1
      if (inc_files_list[pos]->get_url()==url)
Packit df99a1
      {
Packit df99a1
        GPosition this_pos=pos;
Packit df99a1
        ++pos;
Packit df99a1
        inc_files_list.del(this_pos);
Packit df99a1
      } else ++pos;
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  // And update the data.
Packit df99a1
  const GP<ByteStream> str_in(data_pool->get_stream());
Packit df99a1
  const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
Packit df99a1
  IFFByteStream &iff_in=*giff_in;
Packit df99a1
  
Packit df99a1
  const GP<ByteStream> gstr_out(ByteStream::create());
Packit df99a1
  const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
Packit df99a1
  IFFByteStream &iff_out=*giff_out;
Packit df99a1
  
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  if (iff_in.get_chunk(chkid))
Packit df99a1
  {
Packit df99a1
    iff_out.put_chunk(chkid);
Packit df99a1
    int chksize;
Packit df99a1
    while((chksize=iff_in.get_chunk(chkid)))
Packit df99a1
    {
Packit df99a1
      if (chkid!="INCL")
Packit df99a1
      {
Packit df99a1
        iff_out.put_chunk(chkid);
Packit df99a1
        iff_out.copy(*iff_in.get_bytestream());
Packit df99a1
        iff_out.close_chunk();
Packit df99a1
      } else
Packit df99a1
      {
Packit df99a1
        GUTF8String incl_str;
Packit df99a1
        char buffer[1024];
Packit df99a1
        int length;
Packit df99a1
        while((length=iff_in.read(buffer, 1024)))
Packit df99a1
          incl_str+=GUTF8String(buffer, length);
Packit df99a1
        
Packit df99a1
	       // Eat '\n' in the beginning and at the end
Packit df99a1
        while(incl_str.length() && incl_str[0]=='\n')
Packit df99a1
        {
Packit df99a1
          incl_str=incl_str.substr(1,(unsigned int)(-1));
Packit df99a1
        }
Packit df99a1
        while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
Packit df99a1
          incl_str.setat(incl_str.length()-1, 0);
Packit df99a1
        if (incl_str!=id)
Packit df99a1
        {
Packit df99a1
          iff_out.put_chunk("INCL");
Packit df99a1
          iff_out.get_bytestream()->writestring(incl_str);
Packit df99a1
          iff_out.close_chunk();
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      iff_in.close_chunk();
Packit df99a1
    }
Packit df99a1
    iff_out.close_chunk();
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  gstr_out->seek(0, SEEK_SET);
Packit df99a1
  data_pool=DataPool::create(gstr_out);
Packit df99a1
  chunks_number=-1;
Packit df99a1
  
Packit df99a1
  flags|=MODIFIED;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::change_info(GP<DjVuInfo> xinfo,const bool do_reset)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::change_text()\n");
Packit df99a1
  // Mark this as modified
Packit df99a1
  set_modified(true);
Packit df99a1
  if(do_reset)
Packit df99a1
    reset();
Packit df99a1
  info=xinfo;
Packit df99a1
}
Packit df99a1
Packit df99a1
#ifndef NEED_DECODER_ONLY
Packit df99a1
void
Packit df99a1
DjVuFile::change_text(GP<DjVuTXT> txt,const bool do_reset)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::change_text()\n");
Packit df99a1
  GP<DjVuText> gtext_c=DjVuText::create();
Packit df99a1
  DjVuText &text_c=*gtext_c;
Packit df99a1
  if(contains_text())
Packit df99a1
  {
Packit df99a1
    const GP<ByteStream> file_text(get_text());
Packit df99a1
    if(file_text)
Packit df99a1
    {
Packit df99a1
      text_c.decode(file_text);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  GCriticalSectionLock lock(&text_lock);
Packit df99a1
  // Mark this as modified
Packit df99a1
  set_modified(true);
Packit df99a1
  if(do_reset)
Packit df99a1
    reset();
Packit df99a1
  text_c.txt = txt;
Packit df99a1
  text=ByteStream::create();
Packit df99a1
  text_c.encode(text);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuFile::change_meta(const GUTF8String &xmeta,const bool do_reset)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuFile::change_meta()\n");
Packit df99a1
  // Mark this as modified
Packit df99a1
  set_modified(true);
Packit df99a1
  if(contains_meta())
Packit df99a1
  {
Packit df99a1
    (void)get_meta();
Packit df99a1
  }
Packit df99a1
  if(do_reset)
Packit df99a1
    reset();
Packit df99a1
  GCriticalSectionLock lock(&meta_lock);
Packit df99a1
  meta=ByteStream::create();
Packit df99a1
  if(xmeta.length())
Packit df99a1
  {
Packit df99a1
    const GP<IFFByteStream> giff=IFFByteStream::create(meta);
Packit df99a1
    IFFByteStream &iff=*giff;
Packit df99a1
    iff.put_chunk("METz");
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50);
Packit df99a1
      gbsiff->writestring(xmeta);
Packit df99a1
    }
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
Packit df99a1
Packit df99a1
#ifdef HAVE_NAMESPACES
Packit df99a1
}
Packit df99a1
# ifndef NOT_USING_DJVU_NAMESPACE
Packit df99a1
using namespace DJVU;
Packit df99a1
# endif
Packit df99a1
#endif