Blame libdjvu/DjVuDocument.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 "DjVuDocument.h"
Packit df99a1
#include "DjVmDoc.h"
Packit df99a1
#include "DjVmDir0.h"
Packit df99a1
#include "DjVmNav.h"
Packit df99a1
#include "DjVuNavDir.h"
Packit df99a1
#include "DjVuImage.h"
Packit df99a1
#include "DjVuFileCache.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "DataPool.h"
Packit df99a1
#include "IW44Image.h"
Packit df99a1
#include "GRect.h"
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
static const char octets[4]={0x41,0x54,0x26,0x54};
Packit df99a1
const float	DjVuDocument::thumb_gamma=(float)2.20;
Packit df99a1
Packit df99a1
void (* DjVuDocument::djvu_import_codec)(
Packit df99a1
  GP<DataPool> &pool, const GURL &url, bool &needs_compression,
Packit df99a1
  bool &needs_rename )=0;
Packit df99a1
Packit df99a1
void (* DjVuDocument::djvu_compress_codec)(
Packit df99a1
  GP<ByteStream> &doc,const GURL &where,bool bundled)=0;
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::set_import_codec(
Packit df99a1
  void (*codec)(
Packit df99a1
    GP<DataPool> &pool, const GURL &url, bool &needs_compression, bool &needs_rename ))
Packit df99a1
{
Packit df99a1
  djvu_import_codec=codec;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::set_compress_codec(
Packit df99a1
  void (* codec)(
Packit df99a1
    GP<ByteStream> &doc,const GURL &where,bool bundled))
Packit df99a1
{
Packit df99a1
  djvu_compress_codec=codec;
Packit df99a1
}
Packit df99a1
Packit df99a1
DjVuDocument::DjVuDocument(void)
Packit df99a1
  : doc_type(UNKNOWN_TYPE),
Packit df99a1
    needs_compression_flag(false),
Packit df99a1
    can_compress_flag(false),
Packit df99a1
    needs_rename_flag(false),
Packit df99a1
    has_url_names(false),
Packit df99a1
    recover_errors(ABORT),
Packit df99a1
    verbose_eof(false),
Packit df99a1
    init_started(false),
Packit df99a1
    cache(0) 
Packit df99a1
{
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocument>
Packit df99a1
DjVuDocument::create(
Packit df99a1
  GP<DataPool> pool, GP<DjVuPort> xport, DjVuFileCache * const xcache)
Packit df99a1
{
Packit df99a1
  DjVuDocument *doc=new DjVuDocument;
Packit df99a1
  GP<DjVuDocument> retval=doc;
Packit df99a1
  doc->init_data_pool=pool;
Packit df99a1
  doc->start_init(GURL(),xport,xcache);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocument>
Packit df99a1
DjVuDocument::create(
Packit df99a1
  const GP<ByteStream> &bs, GP<DjVuPort> xport, DjVuFileCache * const xcache)
Packit df99a1
{
Packit df99a1
  return create(DataPool::create(bs),xport,xcache);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocument>
Packit df99a1
DjVuDocument::create_wait(
Packit df99a1
  const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache)
Packit df99a1
{
Packit df99a1
  GP<DjVuDocument> retval=create(url,xport,xcache);
Packit df99a1
  retval->wait_for_complete_init();
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::start_init(
Packit df99a1
  const GURL & url, GP<DjVuPort> xport, DjVuFileCache * xcache)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::start_init(): initializing class...\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   if (init_started)
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocument.2nd_init") );
Packit df99a1
   if (!get_count())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocument.not_secure") );
Packit df99a1
   if(url.is_empty())
Packit df99a1
   {
Packit df99a1
     if (!init_data_pool)
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocument.empty_url") );
Packit df99a1
     if(init_url.is_empty())
Packit df99a1
     {
Packit df99a1
       init_url=invent_url("document.djvu");
Packit df99a1
     }
Packit df99a1
   }else
Packit df99a1
   {
Packit df99a1
     init_url=url;
Packit df99a1
   }
Packit df99a1
   
Packit df99a1
      // Initialize
Packit df99a1
   cache=xcache;
Packit df99a1
   doc_type=UNKNOWN_TYPE;
Packit df99a1
   DataPool::close_all();
Packit df99a1
   DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
   if (!xport)
Packit df99a1
     xport=simple_port=new DjVuSimplePort();
Packit df99a1
   pcaster->add_route(this, xport);
Packit df99a1
   pcaster->add_route(this, this);
Packit df99a1
Packit df99a1
   if(!url.is_empty())
Packit df99a1
   {
Packit df99a1
     init_data_pool=pcaster->request_data(this, init_url);
Packit df99a1
     if(init_data_pool)
Packit df99a1
     {
Packit df99a1
       if(!init_url.is_empty() && init_url.is_local_file_url() && djvu_import_codec)
Packit df99a1
       {
Packit df99a1
         djvu_import_codec(init_data_pool,init_url,needs_compression_flag,needs_rename_flag);
Packit df99a1
       }
Packit df99a1
       if(needs_rename_flag)
Packit df99a1
         can_compress_flag=true;
Packit df99a1
     }
Packit df99a1
     if (!init_data_pool) 
Packit df99a1
     {
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+init_url.get_string());
Packit df99a1
     }
Packit df99a1
   }
Packit df99a1
      // Now we say it is ready
Packit df99a1
   init_started=true;
Packit df99a1
Packit df99a1
   init_thread_flags=STARTED;
Packit df99a1
   init_life_saver=this;
Packit df99a1
   init_thr.create(static_init_thread, this);
Packit df99a1
}
Packit df99a1
Packit df99a1
DjVuDocument::~DjVuDocument(void)
Packit df99a1
{
Packit df99a1
      // No more messages, please. We're being destroyed.
Packit df99a1
   get_portcaster()->del_port(this);
Packit df99a1
Packit df99a1
      // We want to stop any DjVuFile which has been created by us
Packit df99a1
      // and is still being decoded. We have to stop them manually because
Packit df99a1
      // they keep the "life saver" in the decoding thread and won't stop
Packit df99a1
      // when we clear the last reference to them
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
      for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
          GP<DjVuFile> file=ufiles_list[pos]->file;
Packit df99a1
          file->stop_decode(false);
Packit df99a1
          file->stop(false);	// Disable any access to data
Packit df99a1
      }
Packit df99a1
      ufiles_list.empty();
Packit df99a1
   }
Packit df99a1
Packit df99a1
   GPList<DjVuPort> ports=get_portcaster()->prefix_to_ports(get_int_prefix());
Packit df99a1
   for(GPosition pos=ports;pos;++pos)
Packit df99a1
   {
Packit df99a1
     GP<DjVuPort> port=ports[pos];
Packit df99a1
     if (port->inherits("DjVuFile"))
Packit df99a1
     {
Packit df99a1
       DjVuFile * file=(DjVuFile *) (DjVuPort *) port;
Packit df99a1
       file->stop_decode(false);
Packit df99a1
       file->stop(false);	// Disable any access to data
Packit df99a1
     }
Packit df99a1
   }
Packit df99a1
   DataPool::close_all();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::stop_init(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::stop_init(): making sure that the init thread dies.\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GMonitorLock lock(&init_thread_flags);
Packit df99a1
   while((init_thread_flags & STARTED) &&
Packit df99a1
	 !(init_thread_flags & FINISHED))
Packit df99a1
   {
Packit df99a1
      if (init_data_pool) init_data_pool->stop(true);	// blocking operation
Packit df99a1
Packit df99a1
      if (ndir_file) ndir_file->stop(false);
Packit df99a1
Packit df99a1
      {
Packit df99a1
	 GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
	 for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
	    ufiles_list[pos]->file->stop(false);	// Disable any access to data
Packit df99a1
	 ufiles_list.empty();
Packit df99a1
      }
Packit df99a1
Packit df99a1
      init_thread_flags.wait(50);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::check() const
Packit df99a1
{
Packit df99a1
  if (!init_started)
Packit df99a1
    G_THROW( ERR_MSG("DjVuDocument.not_init") );
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::static_init_thread(void * cl_data)
Packit df99a1
{
Packit df99a1
  DjVuDocument * th=(DjVuDocument *) cl_data;
Packit df99a1
  GP<DjVuDocument> life_saver=th;
Packit df99a1
  th->init_life_saver=0;
Packit df99a1
  G_TRY {
Packit df99a1
    th->init_thread();
Packit df99a1
  } G_CATCH(exc) {
Packit df99a1
    G_TRY {
Packit df99a1
      int changed = DjVuDocument::DOC_INIT_FAILED;
Packit df99a1
      th->flags |= changed;
Packit df99a1
      get_portcaster()->notify_doc_flags_changed(th, changed, 0);
Packit df99a1
    } G_CATCH_ALL {
Packit df99a1
    } G_ENDCATCH;
Packit df99a1
    G_TRY {
Packit df99a1
      th->check_unnamed_files();
Packit df99a1
      if (!exc.cmp_cause(ByteStream::EndOfFile) && th->verbose_eof)
Packit df99a1
        get_portcaster()->notify_error(th, ERR_MSG("DjVuDocument.init_eof"));
Packit df99a1
      else if (!exc.cmp_cause(DataPool::Stop))
Packit df99a1
        get_portcaster()->notify_status(th, ERR_MSG("DjVuDocument.stopped"));
Packit df99a1
      else
Packit df99a1
        get_portcaster()->notify_error(th, exc.get_cause());
Packit df99a1
    } G_CATCH_ALL {
Packit df99a1
    } G_ENDCATCH;
Packit df99a1
    th->init_thread_flags |= FINISHED;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::init_thread(void)
Packit df99a1
      // This function is run in a separate thread.
Packit df99a1
      // The goal is to detect the document type (BUNDLED, OLD_INDEXED, etc.)
Packit df99a1
      // and decode navigation directory.
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::init_thread(): guessing what we're dealing with\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
      
Packit df99a1
   GP<ByteStream> stream=init_data_pool->get_stream();
Packit df99a1
Packit df99a1
   GP<IFFByteStream> giff=IFFByteStream::create(stream);
Packit df99a1
   IFFByteStream &iff=*giff;
Packit df99a1
   GUTF8String chkid;
Packit df99a1
   int size=iff.get_chunk(chkid);
Packit df99a1
   if (!size)
Packit df99a1
     G_THROW( ByteStream::EndOfFile );
Packit df99a1
   if (size < 0)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocument.no_file") );
Packit df99a1
   if (size<8)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocument.not_DjVu") );
Packit df99a1
   if (chkid=="FORM:DJVM")
Packit df99a1
   {
Packit df99a1
     DEBUG_MSG("Got DJVM document here\n");
Packit df99a1
     DEBUG_MAKE_INDENT(3);
Packit df99a1
     
Packit df99a1
     size=iff.get_chunk(chkid);
Packit df99a1
     if (chkid=="DIRM")
Packit df99a1
       {
Packit df99a1
	 djvm_dir=DjVmDir::create();
Packit df99a1
	 djvm_dir->decode(iff.get_bytestream());
Packit df99a1
	 iff.close_chunk();
Packit df99a1
	 if (djvm_dir->is_bundled())
Packit df99a1
           {
Packit df99a1
             DEBUG_MSG("Got BUNDLED file.\n");
Packit df99a1
             doc_type=BUNDLED;
Packit df99a1
           } 
Packit df99a1
         else
Packit df99a1
           {
Packit df99a1
             DEBUG_MSG("Got INDIRECT file.\n");
Packit df99a1
             doc_type=INDIRECT;
Packit df99a1
           }
Packit df99a1
	 flags|=DOC_TYPE_KNOWN | DOC_DIR_KNOWN;
Packit df99a1
	 pcaster->notify_doc_flags_changed(this, 
Packit df99a1
                                           DOC_TYPE_KNOWN | DOC_DIR_KNOWN, 0);
Packit df99a1
	 check_unnamed_files();
Packit df99a1
         
Packit df99a1
         /* Check for NAVM */
Packit df99a1
         size=iff.get_chunk(chkid);
Packit df99a1
         if (size && chkid=="NAVM")
Packit df99a1
           {
Packit df99a1
             djvm_nav=DjVmNav::create();
Packit df99a1
             djvm_nav->decode(iff.get_bytestream());
Packit df99a1
             iff.close_chunk();
Packit df99a1
           }
Packit df99a1
       }
Packit df99a1
     else if (chkid=="DIR0")
Packit df99a1
       {
Packit df99a1
	 DEBUG_MSG("Got OLD_BUNDLED file.\n");
Packit df99a1
	 doc_type=OLD_BUNDLED;
Packit df99a1
	 flags|=DOC_TYPE_KNOWN;
Packit df99a1
	 pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0);
Packit df99a1
	 check_unnamed_files();
Packit df99a1
       } 
Packit df99a1
     else 
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocument.bad_format") );
Packit df99a1
     
Packit df99a1
     if (doc_type==OLD_BUNDLED)
Packit df99a1
       {
Packit df99a1
         // Read the DjVmDir0 directory. We are unable to tell what
Packit df99a1
         // files are pages and what are included at this point.
Packit df99a1
         // We only know that the first file with DJVU (BM44 or PM44)
Packit df99a1
         // form *is* the first page. The rest will become known
Packit df99a1
         // after we decode DjVuNavDir
Packit df99a1
	 djvm_dir0=DjVmDir0::create();
Packit df99a1
	 djvm_dir0->decode(*iff.get_bytestream());
Packit df99a1
	 iff.close_chunk();
Packit df99a1
         // Get offset to the first DJVU, PM44 or BM44 chunk
Packit df99a1
	 int first_page_offset=0;
Packit df99a1
	 while(!first_page_offset)
Packit df99a1
           {
Packit df99a1
             int offset;
Packit df99a1
             size=iff.get_chunk(chkid, &offset);
Packit df99a1
             if (size==0) G_THROW( ERR_MSG("DjVuDocument.no_page") );
Packit df99a1
             if (chkid=="FORM:DJVU" || 
Packit df99a1
                 chkid=="FORM:PM44" || chkid=="FORM:BM44")
Packit df99a1
               {
Packit df99a1
                 DEBUG_MSG("Got 1st page offset=" << offset << "\n");
Packit df99a1
                 first_page_offset=offset;
Packit df99a1
               }
Packit df99a1
             iff.close_chunk();
Packit df99a1
           }
Packit df99a1
         
Packit df99a1
         // Now get the name of this file
Packit df99a1
	 int file_num;
Packit df99a1
	 for(file_num=0;file_num<djvm_dir0->get_files_num();file_num++)
Packit df99a1
           {
Packit df99a1
             DjVmDir0::FileRec & file=*djvm_dir0->get_file(file_num);
Packit df99a1
             if (file.offset==first_page_offset)
Packit df99a1
               {
Packit df99a1
                 first_page_name=file.name;
Packit df99a1
                 break;
Packit df99a1
               }
Packit df99a1
           }
Packit df99a1
	 if (!first_page_name.length())
Packit df99a1
           G_THROW( ERR_MSG("DjVuDocument.no_page") );
Packit df99a1
	 flags|=DOC_DIR_KNOWN;
Packit df99a1
	 pcaster->notify_doc_flags_changed(this, DOC_DIR_KNOWN, 0);
Packit df99a1
	 check_unnamed_files();
Packit df99a1
       }
Packit df99a1
   } 
Packit df99a1
   else // chkid!="FORM:DJVM"
Packit df99a1
     {
Packit df99a1
       // DJVU format
Packit df99a1
       DEBUG_MSG("Got DJVU OLD_INDEXED or SINGLE_PAGE document here.\n");
Packit df99a1
       doc_type=SINGLE_PAGE;
Packit df99a1
       flags |= DOC_TYPE_KNOWN;
Packit df99a1
       pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0);
Packit df99a1
       check_unnamed_files();
Packit df99a1
     }
Packit df99a1
   if (doc_type==OLD_BUNDLED || doc_type==SINGLE_PAGE)
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("Searching for NDIR chunks...\n");
Packit df99a1
       ndir_file=get_djvu_file(-1);
Packit df99a1
       if (ndir_file) ndir=ndir_file->decode_ndir();
Packit df99a1
       ndir_file=0;	// Otherwise ~DjVuDocument() will stop (=kill) it
Packit df99a1
       if (!ndir)
Packit df99a1
         {
Packit df99a1
           // Seems to be 1-page old-style document. Create dummy NDIR
Packit df99a1
           if (doc_type==OLD_BUNDLED)
Packit df99a1
             {
Packit df99a1
               ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url));
Packit df99a1
               ndir->insert_page(-1, first_page_name);
Packit df99a1
             } 
Packit df99a1
           else
Packit df99a1
             {
Packit df99a1
               ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url.base()));
Packit df99a1
               ndir->insert_page(-1, init_url.fname());
Packit df99a1
             }
Packit df99a1
         } 
Packit df99a1
       else
Packit df99a1
         {
Packit df99a1
           if (doc_type==SINGLE_PAGE)
Packit df99a1
             doc_type=OLD_INDEXED;
Packit df99a1
         }
Packit df99a1
       flags|=DOC_NDIR_KNOWN;
Packit df99a1
       pcaster->notify_doc_flags_changed(this, DOC_NDIR_KNOWN, 0);
Packit df99a1
       check_unnamed_files();
Packit df99a1
     }
Packit df99a1
   
Packit df99a1
   flags |= DOC_INIT_OK;
Packit df99a1
   pcaster->notify_doc_flags_changed(this, DOC_INIT_OK, 0);
Packit df99a1
   check_unnamed_files();
Packit df99a1
   init_thread_flags|=FINISHED;
Packit df99a1
   DEBUG_MSG("DOCUMENT IS FULLY INITIALIZED now: doc_type='" <<
Packit df99a1
	     (doc_type==BUNDLED ? "BUNDLED" :
Packit df99a1
	      doc_type==OLD_BUNDLED ? "OLD_BUNDLED" :
Packit df99a1
	      doc_type==INDIRECT ? "INDIRECT" :
Packit df99a1
	      doc_type==OLD_INDEXED ? "OLD_INDEXED" :
Packit df99a1
	      doc_type==SINGLE_PAGE ? "SINGLE_PAGE" :
Packit df99a1
	      "UNKNOWN") << "'\n");
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuDocument::wait_for_complete_init(void)
Packit df99a1
{
Packit df99a1
  flags.enter();
Packit df99a1
  while(!(flags & DOC_INIT_FAILED) &&
Packit df99a1
        !(flags & DOC_INIT_OK)) flags.wait();
Packit df99a1
  flags.leave();
Packit df99a1
  init_thread_flags.enter();
Packit df99a1
  while (!(init_thread_flags & FINISHED))
Packit df99a1
    init_thread_flags.wait();
Packit df99a1
  init_thread_flags.leave();
Packit df99a1
  return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocument::wait_get_pages_num(void) const
Packit df99a1
{
Packit df99a1
  GSafeFlags &f=const_cast<GSafeFlags &>(flags);
Packit df99a1
  f.enter();
Packit df99a1
  while(!(f & DOC_TYPE_KNOWN) &&
Packit df99a1
        !(f & DOC_INIT_FAILED) &&
Packit df99a1
        !(f & DOC_INIT_OK)) f.wait();
Packit df99a1
  f.leave();
Packit df99a1
  return get_pages_num();
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuDocument::get_int_prefix(void) const
Packit df99a1
{
Packit df99a1
      // These NAMEs are used to enable DjVuFile sharing inside the same
Packit df99a1
      // DjVuDocument using DjVuPortcaster. Since URLs are unique to the
Packit df99a1
      // document, other DjVuDocuments cannot retrieve files until they're
Packit df99a1
      // assigned some permanent name. After '?' there should be the real
Packit df99a1
      // file's URL. Please note, that output of this function is used only
Packit df99a1
      // as name for DjVuPortcaster. Not as a URL.
Packit df99a1
   GUTF8String retval;
Packit df99a1
   return retval.format("document_%p%d?", this, hash(init_url));
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::set_file_aliases(const DjVuFile * file)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::set_file_aliases(): setting global aliases for file '"
Packit df99a1
	     << file->get_url() << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
Packit df99a1
   
Packit df99a1
   GMonitorLock lock(&((DjVuFile *) file)->get_safe_flags());
Packit df99a1
   pcaster->clear_aliases(file);
Packit df99a1
   if (file->is_decode_ok() && cache)
Packit df99a1
   {
Packit df99a1
	 // If file is successfully decoded and caching is enabled,
Packit df99a1
	 // assign a global alias to this file, so that any other
Packit df99a1
	 // DjVuDocument will be able to use it.
Packit df99a1
      
Packit df99a1
      pcaster->add_alias(file, file->get_url().get_string());
Packit df99a1
      if (flags & (DOC_NDIR_KNOWN | DOC_DIR_KNOWN))
Packit df99a1
      {
Packit df99a1
	 int page_num=url_to_page(file->get_url());
Packit df99a1
	 if (page_num>=0)
Packit df99a1
	 {
Packit df99a1
	    if (page_num==0) pcaster->add_alias(file, init_url.get_string()+"#-1");
Packit df99a1
	    pcaster->add_alias(file, init_url.get_string()+"#"+GUTF8String(page_num));
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
	 // The following line MUST stay here. For OLD_INDEXED documents
Packit df99a1
	 // a page may finish decoding before DIR or NDIR becomes known
Packit df99a1
	 // (multithreading, remember), so the code above would not execute
Packit df99a1
      pcaster->add_alias(file, file->get_url().get_string()+"#-1");
Packit df99a1
   } else pcaster->add_alias(file, get_int_prefix()+file->get_url());
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::check_unnamed_files(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocument::check_unnamed_files(): Seeing if we can fix some...\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  
Packit df99a1
  if (flags & DOC_INIT_FAILED)
Packit df99a1
  {
Packit df99a1
    // Init failed. All unnamed files should be terminated
Packit df99a1
    GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
    for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GP<DjVuFile> file=ufiles_list[pos]->file;
Packit df99a1
      file->stop_decode(true);
Packit df99a1
      file->stop(false);	// Disable any access to data
Packit df99a1
    }
Packit df99a1
    ufiles_list.empty();
Packit df99a1
    return;
Packit df99a1
  }
Packit df99a1
  
Packit df99a1
  if ((flags & DOC_TYPE_KNOWN)==0)
Packit df99a1
    return;
Packit df99a1
  
Packit df99a1
  // See the list of unnamed files (created when there was insufficient
Packit df99a1
  // information about DjVuDocument structure) and try to fix those,
Packit df99a1
  // which can be fixed at this time
Packit df99a1
  while(true)
Packit df99a1
  {
Packit df99a1
    DjVuPortcaster * pcaster=get_portcaster();
Packit df99a1
    
Packit df99a1
    GP<UnnamedFile> ufile;
Packit df99a1
    GURL new_url;
Packit df99a1
    GPosition pos ;   
Packit df99a1
	   GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
     for(pos=ufiles_list;pos;)
Packit df99a1
     {
Packit df99a1
	G_TRY
Packit df99a1
        {
Packit df99a1
          GP<UnnamedFile> f=ufiles_list[pos];
Packit df99a1
          if (f->id_type==UnnamedFile::ID) 
Packit df99a1
            new_url=id_to_url(f->id);
Packit df99a1
          else 
Packit df99a1
            new_url=page_to_url(f->page_num);
Packit df99a1
          if (!new_url.is_empty())
Packit df99a1
          {
Packit df99a1
            ufile=f;
Packit df99a1
            // Don't take it off the list. We want to be
Packit df99a1
            // able to stop the init from ~DjVuDocument();
Packit df99a1
            //
Packit df99a1
            // ufiles_list.del(pos);
Packit df99a1
            break;
Packit df99a1
          } else if (is_init_complete())
Packit df99a1
          {
Packit df99a1
            // No empty URLs are allowed at this point.
Packit df99a1
            // We now know all information about the document
Packit df99a1
            // and can determine if a page is inside it or not
Packit df99a1
            f->data_pool->set_eof();
Packit df99a1
            GUTF8String msg;
Packit df99a1
            if (f->id_type==UnnamedFile::ID)
Packit df99a1
              msg= ERR_MSG("DjVuDocument.miss_page_name") "\t"+f->id;
Packit df99a1
            else 
Packit df99a1
              msg= ERR_MSG("DjVuDocument.miss_page_num") "\t"+GUTF8String(f->page_num);
Packit df99a1
            G_THROW(msg);
Packit df99a1
          }
Packit df99a1
          ++pos;
Packit df99a1
        }
Packit df99a1
        G_CATCH(exc)
Packit df99a1
        {
Packit df99a1
          pcaster->notify_error(this, exc.get_cause());
Packit df99a1
          GP<DataPool> pool=ufiles_list[pos]->data_pool;
Packit df99a1
          if (pool)
Packit df99a1
            pool->stop();
Packit df99a1
          GPosition this_pos=pos;
Packit df99a1
          ++pos;
Packit df99a1
          ufiles_list.del(this_pos);
Packit df99a1
        }
Packit df99a1
        G_ENDCATCH;
Packit df99a1
     }
Packit df99a1
     
Packit df99a1
     if (ufile && !new_url.is_empty())
Packit df99a1
       {
Packit df99a1
         DEBUG_MSG("Fixing file: '" << ufile->url << "'=>'" << new_url << "'\n");
Packit df99a1
         // Now, once we know its real URL we can request a real DataPool and
Packit df99a1
         // can connect the DataPool owned by DjVuFile to that real one
Packit df99a1
         // Note, that now request_data() will not play fool because
Packit df99a1
         // we have enough information
Packit df99a1
         
Packit df99a1
         G_TRY
Packit df99a1
           {
Packit df99a1
             if (ufile->data_pool)
Packit df99a1
               {
Packit df99a1
                 GP<DataPool> new_pool=pcaster->request_data(ufile->file, new_url);
Packit df99a1
                 if(!new_pool)
Packit df99a1
                   G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+new_url.get_string());
Packit df99a1
                 ufile->data_pool->connect(new_pool);
Packit df99a1
               }
Packit df99a1
             ufile->file->set_name(new_url.fname());
Packit df99a1
             ufile->file->move(new_url.base());
Packit df99a1
             set_file_aliases(ufile->file);
Packit df99a1
           }
Packit df99a1
         G_CATCH(exc)
Packit df99a1
           {
Packit df99a1
             pcaster->notify_error(this, exc.get_cause());
Packit df99a1
           }   
Packit df99a1
         G_ENDCATCH;
Packit df99a1
       }
Packit df99a1
     else
Packit df99a1
       break;
Packit df99a1
     
Packit df99a1
     // Remove the 'ufile' from the list
Packit df99a1
     for(pos=ufiles_list;pos;++pos)
Packit df99a1
       if (ufiles_list[pos]==ufile)
Packit df99a1
         {
Packit df99a1
           ufiles_list.del(pos);
Packit df99a1
           break;
Packit df99a1
         }
Packit df99a1
  } // while(1)
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocument::get_pages_num(void) const
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  if (flags & DOC_TYPE_KNOWN)
Packit df99a1
    {
Packit df99a1
      if (doc_type==BUNDLED || doc_type==INDIRECT)
Packit df99a1
	return djvm_dir->get_pages_num();
Packit df99a1
      else if (flags & DOC_NDIR_KNOWN)
Packit df99a1
	return ndir->get_pages_num();
Packit df99a1
    }
Packit df99a1
  return 1;
Packit df99a1
}
Packit df99a1
Packit df99a1
GURL
Packit df99a1
DjVuDocument::page_to_url(int page_num) const
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::page_to_url(): page_num=" << page_num << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   GURL url;
Packit df99a1
   if (flags & DOC_TYPE_KNOWN)
Packit df99a1
      switch(doc_type)
Packit df99a1
      {
Packit df99a1
	 case SINGLE_PAGE:
Packit df99a1
         {
Packit df99a1
           if (page_num<1) 
Packit df99a1
             url=init_url;
Packit df99a1
           else
Packit df99a1
             G_THROW( ERR_MSG("DjVuDocument.big_num") );
Packit df99a1
           break;
Packit df99a1
         }
Packit df99a1
	 case OLD_INDEXED:
Packit df99a1
	 {
Packit df99a1
	    if (page_num<0) 
Packit df99a1
              url=init_url;
Packit df99a1
	    else if (flags & DOC_NDIR_KNOWN) 
Packit df99a1
              url=ndir->page_to_url(page_num);
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case OLD_BUNDLED:
Packit df99a1
	 {
Packit df99a1
	    if (page_num<0) 
Packit df99a1
              page_num=0;
Packit df99a1
	    if (page_num==0 && (flags & DOC_DIR_KNOWN))
Packit df99a1
              url=GURL::UTF8(first_page_name,init_url);
Packit df99a1
	    else if (flags & DOC_NDIR_KNOWN)
Packit df99a1
              url=ndir->page_to_url(page_num);
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case BUNDLED:
Packit df99a1
	 {
Packit df99a1
	    if (page_num<0)
Packit df99a1
              page_num=0;
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	      GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num);
Packit df99a1
	      if (!file) 
Packit df99a1
                G_THROW( ERR_MSG("DjVuDocument.big_num") );
Packit df99a1
	      url=GURL::UTF8(file->get_load_name(),init_url);
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case INDIRECT:
Packit df99a1
	 {
Packit df99a1
	    if (page_num<0) page_num=0;
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num);
Packit df99a1
	       if (!file)
Packit df99a1
                 G_THROW( ERR_MSG("DjVuDocument.big_num") );
Packit df99a1
	       url=GURL::UTF8(file->get_load_name(),init_url.base());
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 default:
Packit df99a1
	    G_THROW( ERR_MSG("DjVuDocument.unk_type") );
Packit df99a1
      }
Packit df99a1
   return url;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocument::url_to_page(const GURL & url) const
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::url_to_page(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   int page_num=-1;
Packit df99a1
   if (flags & DOC_TYPE_KNOWN)
Packit df99a1
      switch(doc_type)
Packit df99a1
      {
Packit df99a1
	 case SINGLE_PAGE:
Packit df99a1
	 case OLD_BUNDLED:
Packit df99a1
	 case OLD_INDEXED:
Packit df99a1
	 {
Packit df99a1
	    if (flags & DOC_NDIR_KNOWN) page_num=ndir->url_to_page(url);
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case BUNDLED:
Packit df99a1
	 {
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       GP<DjVmDir::File> file;
Packit df99a1
	       if (url.base()==init_url)
Packit df99a1
                 file=djvm_dir->id_to_file(url.fname());
Packit df99a1
	       if (file)
Packit df99a1
                 page_num=file->get_page_num();
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case INDIRECT:
Packit df99a1
	 {
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       GP<DjVmDir::File> file;
Packit df99a1
	       if (url.base()==init_url.base())
Packit df99a1
                 file=djvm_dir->id_to_file(url.fname());
Packit df99a1
	       if (file)
Packit df99a1
                 page_num=file->get_page_num();
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 default:
Packit df99a1
	    G_THROW( ERR_MSG("DjVuDocument.unk_type") );
Packit df99a1
      }
Packit df99a1
   return page_num;
Packit df99a1
}
Packit df99a1
Packit df99a1
GURL
Packit df99a1
DjVuDocument::id_to_url(const GUTF8String & id) const
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::id_to_url(): translating ID='" << id << "' to URL\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (flags & DOC_TYPE_KNOWN)
Packit df99a1
      switch(doc_type)
Packit df99a1
      {
Packit df99a1
	 case BUNDLED:
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	      GP<DjVmDir::File> file=djvm_dir->id_to_file(id);
Packit df99a1
	      if (!file)
Packit df99a1
              {
Packit df99a1
                file=djvm_dir->name_to_file(id);
Packit df99a1
	        if (!file)
Packit df99a1
                  file=djvm_dir->title_to_file(id);
Packit df99a1
              }
Packit df99a1
	      if (file)
Packit df99a1
	        return GURL::UTF8(file->get_load_name(),init_url);
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 case INDIRECT:
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       GP<DjVmDir::File> file=djvm_dir->id_to_file(id);
Packit df99a1
	       if (!file)
Packit df99a1
               {
Packit df99a1
                 file=djvm_dir->name_to_file(id);
Packit df99a1
	         if (!file)
Packit df99a1
                   file=djvm_dir->title_to_file(id);
Packit df99a1
               }
Packit df99a1
	       if (file)
Packit df99a1
	         return GURL::UTF8(file->get_load_name(),init_url.base());
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 case OLD_BUNDLED:
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       GP<DjVmDir0::FileRec> frec=djvm_dir0->get_file(id);
Packit df99a1
	       if (frec)
Packit df99a1
                 return GURL::UTF8(id,init_url);
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 case OLD_INDEXED:
Packit df99a1
	 case SINGLE_PAGE:
Packit df99a1
	    return GURL::UTF8(id,init_url.base());
Packit df99a1
	    break;
Packit df99a1
      }
Packit df99a1
   return GURL();
Packit df99a1
}
Packit df99a1
Packit df99a1
GURL
Packit df99a1
DjVuDocument::id_to_url(const DjVuPort * source, const GUTF8String &id)
Packit df99a1
{
Packit df99a1
   return id_to_url(id);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocument::url_to_file(const GURL & url, bool dont_create) const
Packit df99a1
      // This function is private and is called from two places:
Packit df99a1
      // id_to_file() and get_djvu_file() ONLY when the structure is known
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::url_to_file(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // Try DjVuPortcaster to find existing files.
Packit df99a1
   DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
Packit df99a1
   GP<DjVuPort> port;
Packit df99a1
Packit df99a1
   if (cache)
Packit df99a1
   {
Packit df99a1
	 // First - fully decoded files
Packit df99a1
      port=pcaster->alias_to_port(url.get_string());
Packit df99a1
      if (port && port->inherits("DjVuFile"))
Packit df99a1
      {
Packit df99a1
	 DEBUG_MSG("found fully decoded file using DjVuPortcaster\n");
Packit df99a1
	 return (DjVuFile *) (DjVuPort *) port;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Second - internal files
Packit df99a1
   port=pcaster->alias_to_port(get_int_prefix()+url);
Packit df99a1
   if (port && port->inherits("DjVuFile"))
Packit df99a1
   {
Packit df99a1
      DEBUG_MSG("found internal file using DjVuPortcaster\n");
Packit df99a1
      return (DjVuFile *) (DjVuPort *) port;
Packit df99a1
   }
Packit df99a1
Packit df99a1
   GP<DjVuFile> file;
Packit df99a1
   
Packit df99a1
   if (!dont_create)
Packit df99a1
   {
Packit df99a1
      DEBUG_MSG("creating a new file\n");
Packit df99a1
      file=DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof);
Packit df99a1
      const_cast<DjVuDocument *>(this)->set_file_aliases(file);
Packit df99a1
   }
Packit df99a1
Packit df99a1
   return file;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocument::get_djvu_file(int page_num, bool dont_create) const
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_djvu_file(): request for page " << page_num << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
Packit df99a1
   
Packit df99a1
   GURL url;
Packit df99a1
   {
Packit df99a1
	 // I'm locking the flags because depending on what page_to_url()
Packit df99a1
	 // returns me, I'll be creating DjVuFile in different ways.
Packit df99a1
	 // And I don't want the situation to change between the moment I call
Packit df99a1
	 // id_to_url() and I actually create DjVuFile
Packit df99a1
      GMonitorLock lock(&(const_cast<DjVuDocument *>(this)->flags));
Packit df99a1
      url=page_to_url(page_num);
Packit df99a1
      if (url.is_empty())
Packit df99a1
      {
Packit df99a1
	    // If init is complete and url is empty, we know for sure, that
Packit df99a1
	    // smth is wrong with the page_num. So we can return ZERO.
Packit df99a1
	    // Otherwise we create a temporary file and wait for init to finish
Packit df99a1
	 if (is_init_complete()) return 0;
Packit df99a1
	 
Packit df99a1
	 DEBUG_MSG("Structure is not known => check <doc_url>#<page_num> alias...\n");
Packit df99a1
	 GP<DjVuPort> port;
Packit df99a1
	 if (cache)
Packit df99a1
	    port=pcaster->alias_to_port(init_url.get_string()+"#"+GUTF8String(page_num));
Packit df99a1
	 if (!port || !port->inherits("DjVuFile"))
Packit df99a1
	 {
Packit df99a1
	    DEBUG_MSG("failed => invent dummy URL and proceed\n");
Packit df99a1
	 
Packit df99a1
	       // Invent some dummy temporary URL. I don't care what it will
Packit df99a1
	       // be. I'll remember the page_num and will generate the correct URL
Packit df99a1
	       // after I learn what the document is
Packit df99a1
            GUTF8String name("page");
Packit df99a1
            name+=GUTF8String(page_num);
Packit df99a1
            name+=".djvu";
Packit df99a1
            url=invent_url(name);
Packit df99a1
Packit df99a1
            GCriticalSectionLock(&(const_cast<DjVuDocument *>(this)->ufiles_lock));
Packit df99a1
	    for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
	    {
Packit df99a1
	       GP<UnnamedFile> f=ufiles_list[pos];
Packit df99a1
	       if (f->url==url) return f->file;
Packit df99a1
	    }
Packit df99a1
	    GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::PAGE_NUM, 0,
Packit df99a1
						  page_num, url, 0);
Packit df99a1
Packit df99a1
	       // We're adding the record to the list before creating the DjVuFile
Packit df99a1
	       // because DjVuFile::init() will call request_data(), and the
Packit df99a1
	       // latter should be able to find the record.
Packit df99a1
	       //
Packit df99a1
	       // We also want to keep ufiles_lock to make sure that when
Packit df99a1
	       // request_data() is called, the record is still there
Packit df99a1
	    const_cast<DjVuDocument *>(this)->ufiles_list.append(ufile);
Packit df99a1
      
Packit df99a1
	    GP<DjVuFile> file=
Packit df99a1
              DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof);
Packit df99a1
	    ufile->file=file;
Packit df99a1
	    return file;
Packit df99a1
	 } else url=((DjVuFile *) (DjVuPort *) port)->get_url();
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
   
Packit df99a1
   GP<DjVuFile> file=url_to_file(url, dont_create);
Packit df99a1
   if (file) 
Packit df99a1
     pcaster->add_route(file, const_cast<DjVuDocument *>(this));
Packit df99a1
   return file;
Packit df99a1
}
Packit df99a1
Packit df99a1
GURL
Packit df99a1
DjVuDocument::invent_url(const GUTF8String &name) const
Packit df99a1
{
Packit df99a1
   GUTF8String buffer;
Packit df99a1
   buffer.format("djvufileurl://%p/%s", this, (const char *)name);
Packit df99a1
   return GURL::UTF8(buffer);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocument::get_djvu_file(const GUTF8String& id, bool dont_create)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
  DEBUG_MSG("DjVuDocument::get_djvu_file(): ID='" << id << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  if (!id.length())
Packit df99a1
    return get_djvu_file(-1);
Packit df99a1
Packit df99a1
// Integers are not supported, only ID's  
Packit df99a1
//  if (id.is_int())
Packit df99a1
//     return get_djvu_file(id.toInt(),dont_create);
Packit df99a1
  
Packit df99a1
  GURL url;
Packit df99a1
  // I'm locking the flags because depending on what id_to_url()
Packit df99a1
  // returns me, I'll be creating DjVuFile in different ways.
Packit df99a1
  // And I don't want the situation to change between the moment I call
Packit df99a1
  // id_to_url() and I actually create DjVuFile
Packit df99a1
  {
Packit df99a1
    GMonitorLock lock(&flags);
Packit df99a1
    url=id_to_url(id);
Packit df99a1
    if(url.is_empty() && !id.is_int())
Packit df99a1
    {
Packit df99a1
      // If init is complete, we know for sure, that there is no such
Packit df99a1
      // file with ID 'id' in the document. Otherwise we have to
Packit df99a1
      // create a temporary file and wait for the init to finish
Packit df99a1
      if (is_init_complete())
Packit df99a1
        return 0;
Packit df99a1
      // Invent some dummy temporary URL. I don't care what it will
Packit df99a1
      // be. I'll remember the ID and will generate the correct URL
Packit df99a1
      // after I learn what the document is
Packit df99a1
      url=invent_url(id);
Packit df99a1
      DEBUG_MSG("Invented url='" << url << "'\n");
Packit df99a1
Packit df99a1
      GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
      for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
        GP<UnnamedFile> f=ufiles_list[pos];
Packit df99a1
        if (f->url==url)
Packit df99a1
          return f->file;
Packit df99a1
      }
Packit df99a1
      GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::ID, id, 0, url, 0);
Packit df99a1
Packit df99a1
      // We're adding the record to the list before creating the DjVuFile
Packit df99a1
      // because DjVuFile::init() will call request_data(), and the
Packit df99a1
      // latter should be able to find the record.
Packit df99a1
      //
Packit df99a1
      // We also want to keep ufiles_lock to make sure that when
Packit df99a1
      // request_data() is called, the record is still there
Packit df99a1
      ufiles_list.append(ufile);
Packit df99a1
      
Packit df99a1
      GP<DjVuFile> file=DjVuFile::create(url,this,recover_errors,verbose_eof);
Packit df99a1
      ufile->file=file;
Packit df99a1
      return file;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
     
Packit df99a1
  return get_djvu_file(url,dont_create);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocument::get_djvu_file(const GURL& url, bool dont_create)
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_djvu_file(): URL='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (url.is_empty())
Packit df99a1
     return 0;
Packit df99a1
Packit df99a1
   const GP<DjVuFile> file(url_to_file(url, dont_create));
Packit df99a1
Packit df99a1
   if (file)
Packit df99a1
     get_portcaster()->add_route(file, this);
Packit df99a1
Packit df99a1
   return file;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuImage>
Packit df99a1
DjVuDocument::get_page(int page_num, bool sync, DjVuPort * port) const
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_page(): request for page " << page_num << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GP<DjVuImage> dimg;
Packit df99a1
   const GP<DjVuFile> file(get_djvu_file(page_num));
Packit df99a1
   if (file)
Packit df99a1
   {
Packit df99a1
     dimg=DjVuImage::create(file);
Packit df99a1
     if (port)
Packit df99a1
       DjVuPort::get_portcaster()->add_route(dimg, port);
Packit df99a1
   
Packit df99a1
     file->resume_decode();
Packit df99a1
     if (dimg && sync)
Packit df99a1
       dimg->wait_for_complete_decode();
Packit df99a1
   }
Packit df99a1
   return dimg;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuImage>
Packit df99a1
DjVuDocument::get_page(const GUTF8String &id, bool sync, DjVuPort * port)
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_page(): ID='" << id << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GP<DjVuImage> dimg;
Packit df99a1
   const GP<DjVuFile> file(get_djvu_file(id));
Packit df99a1
   if(file)
Packit df99a1
   {
Packit df99a1
     dimg=DjVuImage::create(file);
Packit df99a1
     if (port)
Packit df99a1
       DjVuPort::get_portcaster()->add_route(dimg, port);
Packit df99a1
   
Packit df99a1
     file->resume_decode();
Packit df99a1
     if (dimg && sync)
Packit df99a1
       dimg->wait_for_complete_decode();
Packit df99a1
   }
Packit df99a1
   return dimg;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::process_threqs(void)
Packit df99a1
      // Will look thru threqs_list and try to fulfil every request
Packit df99a1
{
Packit df99a1
  GCriticalSectionLock lock(&threqs_lock);
Packit df99a1
  for(GPosition pos=threqs_list;pos;)
Packit df99a1
  {
Packit df99a1
    GP<ThumbReq> req=threqs_list[pos];
Packit df99a1
    bool remove=false;
Packit df99a1
    if (req->thumb_file)
Packit df99a1
    {
Packit df99a1
      G_TRY {
Packit df99a1
	       // There is supposed to be a file with thumbnails
Packit df99a1
        if (req->thumb_file->is_data_present())
Packit df99a1
        {
Packit df99a1
          // Cool, we can extract the thumbnail now
Packit df99a1
          GP<ByteStream> str=req->thumb_file->get_init_data_pool()->get_stream();
Packit df99a1
          GP<IFFByteStream> giff=IFFByteStream::create(str);
Packit df99a1
          IFFByteStream &iff=*giff;
Packit df99a1
          GUTF8String chkid;
Packit df99a1
          if (!iff.get_chunk(chkid) || chkid!="FORM:THUM")
Packit df99a1
            G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );          
Packit df99a1
          for(int i=0;i<req->thumb_chunk;i++)
Packit df99a1
          {
Packit df99a1
            if (!iff.get_chunk(chkid)) 
Packit df99a1
              G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );
Packit df99a1
            iff.close_chunk();
Packit df99a1
          }
Packit df99a1
          if (!iff.get_chunk(chkid) || chkid!="TH44")
Packit df99a1
            G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );
Packit df99a1
          
Packit df99a1
          // Copy the data
Packit df99a1
          char buffer[1024];
Packit df99a1
          int length;
Packit df99a1
          while((length=iff.read(buffer, 1024)))
Packit df99a1
            req->data_pool->add_data(buffer, length);
Packit df99a1
          req->data_pool->set_eof();
Packit df99a1
          
Packit df99a1
          // Also add this file to cache so that we won't have
Packit df99a1
          // to download it next time
Packit df99a1
          add_to_cache(req->thumb_file);
Packit df99a1
          req->thumb_file=0;
Packit df99a1
          req->image_file=0;
Packit df99a1
          remove=true;
Packit df99a1
        }
Packit df99a1
      } G_CATCH(exc) {
Packit df99a1
        GUTF8String msg= ERR_MSG("DjVuDocument.cant_extract") "\n";
Packit df99a1
        msg+=exc.get_cause();
Packit df99a1
        get_portcaster()->notify_error(this, msg);
Packit df99a1
	       // Switch this request to the "decoding" mode
Packit df99a1
        req->image_file=get_djvu_file(req->page_num);
Packit df99a1
        req->thumb_file=0;
Packit df99a1
        req->data_pool->set_eof();
Packit df99a1
        remove=true;
Packit df99a1
      } G_ENDCATCH;
Packit df99a1
    } // if (req->thumb_file)
Packit df99a1
    
Packit df99a1
    if (req->image_file)
Packit df99a1
    {
Packit df99a1
      G_TRY {
Packit df99a1
	       // Decode the file if necessary. Or just used predecoded image.
Packit df99a1
        GSafeFlags & file_flags=req->image_file->get_safe_flags();
Packit df99a1
        {
Packit df99a1
          GMonitorLock lock(&file_flags);
Packit df99a1
          if (!req->image_file->is_decoding())
Packit df99a1
          {
Packit df99a1
            if (req->image_file->is_decode_ok())
Packit df99a1
            {
Packit df99a1
              // We can generate it now
Packit df99a1
              const GP<DjVuImage> dimg(DjVuImage::create(req->image_file));
Packit df99a1
              
Packit df99a1
              dimg->wait_for_complete_decode();
Packit df99a1
              
Packit df99a1
              int width = 160;
Packit df99a1
              int height = 160;
Packit df99a1
              
Packit df99a1
              if( dimg->get_width() )
Packit df99a1
                width = dimg->get_width();
Packit df99a1
              if( dimg->get_height() )
Packit df99a1
                height = dimg->get_height();
Packit df99a1
              
Packit df99a1
              GRect rect(0, 0, 160, height*160/width);
Packit df99a1
              GP<GPixmap> pm=dimg->get_pixmap(rect, rect, thumb_gamma);
Packit df99a1
              if (!pm)
Packit df99a1
              {
Packit df99a1
                GP<GBitmap> bm=dimg->get_bitmap(rect, rect, sizeof(int));
Packit df99a1
                if(bm)
Packit df99a1
                  pm=GPixmap::create(*bm);
Packit df99a1
                else
Packit df99a1
                  pm = GPixmap::create(rect.height(), rect.width(), 
Packit df99a1
                                       &GPixel::WHITE);
Packit df99a1
              }
Packit df99a1
              
Packit df99a1
              // Store and compress the pixmap
Packit df99a1
              GP<IW44Image> iwpix=IW44Image::create_encode(*pm);
Packit df99a1
              GP<ByteStream> gstr=ByteStream::create();
Packit df99a1
              IWEncoderParms parms;
Packit df99a1
              parms.slices=97;
Packit df99a1
              parms.bytes=0;
Packit df99a1
              parms.decibels=0;
Packit df99a1
              iwpix->encode_chunk(gstr, parms);
Packit df99a1
              TArray<char> data=gstr->get_data();
Packit df99a1
              
Packit df99a1
              req->data_pool->add_data((const char *) data, data.size());
Packit df99a1
              req->data_pool->set_eof();
Packit df99a1
              
Packit df99a1
              req->thumb_file=0;
Packit df99a1
              req->image_file=0;
Packit df99a1
              remove=true;
Packit df99a1
            } else if (req->image_file->is_decode_failed())
Packit df99a1
            {
Packit df99a1
              // Unfortunately we cannot decode it
Packit df99a1
              req->thumb_file=0;
Packit df99a1
              req->image_file=0;
Packit df99a1
              req->data_pool->set_eof();
Packit df99a1
              remove=true;
Packit df99a1
            } else
Packit df99a1
            {
Packit df99a1
              req->image_file->start_decode();
Packit df99a1
            }
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
      } G_CATCH(exc) {
Packit df99a1
        GUTF8String msg="Failed to decode thumbnails:\n";
Packit df99a1
        msg+=exc.get_cause();
Packit df99a1
        get_portcaster()->notify_error(this, msg);
Packit df99a1
        
Packit df99a1
	       // Get rid of this request
Packit df99a1
        req->image_file=0;
Packit df99a1
        req->thumb_file=0;
Packit df99a1
        req->data_pool->set_eof();
Packit df99a1
        remove=true;
Packit df99a1
      } G_ENDCATCH;
Packit df99a1
    }
Packit df99a1
    
Packit df99a1
    if (remove)
Packit df99a1
    {
Packit df99a1
      GPosition this_pos=pos;
Packit df99a1
      ++pos;
Packit df99a1
      threqs_list.del(this_pos);
Packit df99a1
    } else ++pos;
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocument::ThumbReq>
Packit df99a1
DjVuDocument::add_thumb_req(const GP<ThumbReq> & thumb_req)
Packit df99a1
      // Will look through the list of pending requests for thumbnails
Packit df99a1
      // and try to add the specified request. If a duplicate is found,
Packit df99a1
      // it will be returned and the list will not be modified
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lock(&threqs_lock);
Packit df99a1
   for(GPosition pos=threqs_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      GP<ThumbReq> req=threqs_list[pos];
Packit df99a1
      if (req->page_num==thumb_req->page_num)
Packit df99a1
	 return req;
Packit df99a1
   }
Packit df99a1
   threqs_list.append(thumb_req);
Packit df99a1
   return thumb_req;
Packit df99a1
}
Packit df99a1
Packit df99a1
GList<GUTF8String>
Packit df99a1
DjVuDocument::get_id_list(void)
Packit df99a1
{
Packit df99a1
  GList<GUTF8String> ids;
Packit df99a1
  if (is_init_complete())
Packit df99a1
  {
Packit df99a1
    if(djvm_dir)
Packit df99a1
    {
Packit df99a1
      GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
Packit df99a1
      for(GPosition pos=files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
        ids.append(files_list[pos]->get_load_name());
Packit df99a1
      }
Packit df99a1
    }else
Packit df99a1
    {
Packit df99a1
      const int page_num=get_pages_num();
Packit df99a1
      for(int page=0;page
Packit df99a1
      { 
Packit df99a1
        ids.append(page_to_url(page).fname());
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  return ids;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::map_ids(GMap<GUTF8String,void *> &map)
Packit df99a1
{
Packit df99a1
  GList<GUTF8String> ids=get_id_list();
Packit df99a1
  for(GPosition pos=ids;pos;++pos)
Packit df99a1
  {
Packit df99a1
    map[ids[pos]]=0;
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuDocument::get_thumbnail(int page_num, bool dont_decode)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_thumbnail(): page_num=" << page_num << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (!is_init_complete()) return 0;
Packit df99a1
   
Packit df99a1
   {
Packit df99a1
	 // See if we already have request for this thumbnail pending
Packit df99a1
      GCriticalSectionLock lock(&threqs_lock);
Packit df99a1
      for(GPosition pos=threqs_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
	 GP<ThumbReq> req=threqs_list[pos];
Packit df99a1
	 if (req->page_num==page_num)
Packit df99a1
	    return req->data_pool;	// That's it. Just return it.
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // No pending request for this page... Create one
Packit df99a1
   GP<ThumbReq> thumb_req=new ThumbReq(page_num, DataPool::create());
Packit df99a1
   
Packit df99a1
      // First try to find predecoded thumbnail
Packit df99a1
   if (get_doc_type()==INDIRECT || get_doc_type()==BUNDLED)
Packit df99a1
   {
Packit df99a1
	 // Predecoded thumbnails exist for new formats only
Packit df99a1
      GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
Packit df99a1
      GP<DjVmDir::File> thumb_file;
Packit df99a1
      int thumb_start=0;
Packit df99a1
      int page_cnt=-1;
Packit df99a1
      for(GPosition pos=files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
	 GP<DjVmDir::File> f=files_list[pos];
Packit df99a1
	 if (f->is_thumbnails())
Packit df99a1
	 {
Packit df99a1
	    thumb_file=f;
Packit df99a1
	    thumb_start=page_cnt+1;
Packit df99a1
	 } else if (f->is_page())
Packit df99a1
         {
Packit df99a1
           page_cnt++;
Packit df99a1
         }
Packit df99a1
	 if (page_cnt==page_num) break;
Packit df99a1
      }
Packit df99a1
      if (thumb_file)
Packit df99a1
      {
Packit df99a1
	    // That's the file with the desired thumbnail image
Packit df99a1
	 thumb_req->thumb_file=get_djvu_file(thumb_file->get_load_name());
Packit df99a1
	 thumb_req->thumb_chunk=page_num-thumb_start;
Packit df99a1
	 thumb_req=add_thumb_req(thumb_req);
Packit df99a1
	 process_threqs();
Packit df99a1
	 return thumb_req->data_pool;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Apparently we're out of luck and need to decode the requested
Packit df99a1
      // page (unless it's already done and if it's allowed) and render
Packit df99a1
      // it into the thumbnail. If dont_decode is true, do not attempt
Packit df99a1
      // to create this file (because this will result in a request for data)
Packit df99a1
   GP<DjVuFile> file=get_djvu_file(page_num, dont_decode);
Packit df99a1
   if (file)
Packit df99a1
   {
Packit df99a1
      thumb_req->image_file=file;
Packit df99a1
Packit df99a1
	 // I'm locking the flags here to make sure, that DjVuFile will not
Packit df99a1
	 // change its state in between of the checks.
Packit df99a1
      GSafeFlags & file_flags=file->get_safe_flags();
Packit df99a1
      {
Packit df99a1
	 GMonitorLock lock(&file_flags);
Packit df99a1
	 if (thumb_req->image_file->is_decode_ok() || !dont_decode)
Packit df99a1
	 {
Packit df99a1
	       // Just add it to the list and call process_threqs(). It
Packit df99a1
	       // will start decoding if necessary
Packit df99a1
	    thumb_req=add_thumb_req(thumb_req);
Packit df99a1
	    process_threqs();
Packit df99a1
	 } else
Packit df99a1
	 {
Packit df99a1
	       // Nothing can be done return ZERO
Packit df99a1
	    thumb_req=0;
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
   } else thumb_req=0;
Packit df99a1
   
Packit df99a1
   if (thumb_req) return thumb_req->data_pool;
Packit df99a1
   else return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
add_to_cache(const GP<DjVuFile> & f, GMap<GURL, void *> & map,
Packit df99a1
	     DjVuFileCache * cache)
Packit df99a1
{
Packit df99a1
   GURL url=f->get_url();
Packit df99a1
   DEBUG_MSG("DjVuDocument::add_to_cache(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   if (!map.contains(url))
Packit df99a1
   {
Packit df99a1
      map[url]=0;
Packit df99a1
      cache->add_file(f);
Packit df99a1
      
Packit df99a1
      GPList<DjVuFile> list;
Packit df99a1
      for(GPosition pos=list;pos;++pos)
Packit df99a1
	 add_to_cache(list[pos], map, cache);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::add_to_cache(const GP<DjVuFile> & f)
Packit df99a1
{
Packit df99a1
   if (cache)
Packit df99a1
   {
Packit df99a1
      GMap<GURL, void *> map;
Packit df99a1
      ::add_to_cache(f, map, cache);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::notify_file_flags_changed(const DjVuFile * source,
Packit df99a1
					long set_mask, long clr_mask)
Packit df99a1
{
Packit df99a1
      // Don't check here if the document is initialized or not.
Packit df99a1
      // This function may be called when it's not.
Packit df99a1
      // check();
Packit df99a1
   if (set_mask & DjVuFile::DECODE_OK)
Packit df99a1
   {
Packit df99a1
      set_file_aliases(source);
Packit df99a1
      if (cache) add_to_cache((DjVuFile *) source);
Packit df99a1
      if(!needs_compression_flag)
Packit df99a1
      {
Packit df99a1
        if(source->needs_compression())
Packit df99a1
        {
Packit df99a1
          can_compress_flag=true;
Packit df99a1
          needs_compression_flag=true;
Packit df99a1
        }else if(source->can_compress())
Packit df99a1
        {
Packit df99a1
          can_compress_flag=true;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
   process_threqs();
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocument::id_to_file(const DjVuPort * source, const GUTF8String &id)
Packit df99a1
{
Packit df99a1
   return (DjVuFile *) get_djvu_file(id);
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuDocument::request_data(const DjVuPort * source, const GURL & url)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::request_data(): seeing if we can do it\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (url==init_url)
Packit df99a1
     return init_data_pool;
Packit df99a1
Packit df99a1
   check();	// Don't put it before 'init_data_pool'
Packit df99a1
Packit df99a1
   {
Packit df99a1
	 // See if there is a file in the "UnnamedFiles" list.
Packit df99a1
	 // If it's there, then create an empty DataPool and store its
Packit df99a1
	 // pointer in the list. The "init thread" will eventually
Packit df99a1
	 // do smth with it.
Packit df99a1
      GCriticalSectionLock lock(&ufiles_lock);
Packit df99a1
      for(GPosition pos=ufiles_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
	 GP<UnnamedFile> f=ufiles_list[pos];
Packit df99a1
	 if (f->url==url)
Packit df99a1
	 {
Packit df99a1
	    DEBUG_MSG("Found tmp unnamed DjVuFile. Return empty DataPool\n");
Packit df99a1
	       // Remember the DataPool. We will connect it to the
Packit df99a1
	       // actual data after the document structure becomes known
Packit df99a1
	    f->data_pool=DataPool::create();
Packit df99a1
	    return f->data_pool;
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Well, the url is not in the "UnnamedFiles" list, but it doesn't
Packit df99a1
      // mean, that it's not "artificial". Stay alert!
Packit df99a1
   GP<DataPool> data_pool;
Packit df99a1
   if (flags & DOC_TYPE_KNOWN)
Packit df99a1
      switch(doc_type)
Packit df99a1
      {
Packit df99a1
	 case OLD_BUNDLED:
Packit df99a1
	 {
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       DEBUG_MSG("The document is in OLD_BUNDLED format\n");
Packit df99a1
	       if (url.base()!=init_url)
Packit df99a1
		        G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"+url.get_string());
Packit df99a1
	 
Packit df99a1
	       GP<DjVmDir0::FileRec> file=djvm_dir0->get_file(url.fname());
Packit df99a1
	       if (!file)
Packit df99a1
               {
Packit df99a1
                 G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname());
Packit df99a1
               }
Packit df99a1
	       data_pool=DataPool::create(init_data_pool, file->offset, file->size);
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case BUNDLED:
Packit df99a1
	 {
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	    {
Packit df99a1
	       DEBUG_MSG("The document is in new BUNDLED format\n");
Packit df99a1
	       if (url.base()!=init_url)
Packit df99a1
               {
Packit df99a1
		 G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"
Packit df99a1
                   +url.get_string());
Packit df99a1
               }
Packit df99a1
	 
Packit df99a1
	       GP<DjVmDir::File> file=djvm_dir->id_to_file(url.fname());
Packit df99a1
	       if (!file)
Packit df99a1
               {
Packit df99a1
                 G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname());
Packit df99a1
               }
Packit df99a1
	       data_pool=DataPool::create(init_data_pool, file->offset, file->size);
Packit df99a1
	    }
Packit df99a1
	    break;
Packit df99a1
	 }
Packit df99a1
	 case SINGLE_PAGE:
Packit df99a1
	 case OLD_INDEXED:
Packit df99a1
	 case INDIRECT:
Packit df99a1
	 {
Packit df99a1
	    DEBUG_MSG("The document is in SINGLE_PAGE or OLD_INDEXED or INDIRECT format\n");
Packit df99a1
	    if (flags & DOC_DIR_KNOWN)
Packit df99a1
	       if (doc_type==INDIRECT && !djvm_dir->id_to_file(url.fname()))
Packit df99a1
		        G_THROW( ERR_MSG("DjVuDocument.URL_outside2") "\t"+url.get_string());
Packit df99a1
	 
Packit df99a1
	    if (url.is_local_file_url())
Packit df99a1
	    {
Packit df99a1
//	       GUTF8String fname=GOS::url_to_filename(url);
Packit df99a1
//	       if (GOS::basename(fname)=="-") fname="-";
Packit df99a1
	       DEBUG_MSG("url=" << url << "\n");
Packit df99a1
Packit df99a1
	       data_pool=DataPool::create(url);
Packit df99a1
	    }
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
   return data_pool;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
static void
Packit df99a1
add_file_to_djvm(const GP<DjVuFile> & file, bool page,
Packit df99a1
		 DjVmDoc & doc, GMap<GURL, void *> & map)
Packit df99a1
      // This function is used only for obsolete formats.
Packit df99a1
      // For new formats there is no need to process files recursively.
Packit df99a1
      // All information is already available from the DJVM chunk
Packit df99a1
{
Packit df99a1
   GURL url=file->get_url();
Packit df99a1
Packit df99a1
   if (!map.contains(url))
Packit df99a1
   {
Packit df99a1
      map[url]=0;
Packit df99a1
Packit df99a1
      if (file->get_chunks_number()>0 && !file->contains_chunk("NDIR"))
Packit df99a1
      {
Packit df99a1
	    // Get the data and unlink any file containing NDIR chunk.
Packit df99a1
	    // Yes. We're lazy. We don't check if those files contain
Packit df99a1
	    // anything else.
Packit df99a1
	 GPosition pos;
Packit df99a1
	 GPList<DjVuFile> files_list=file->get_included_files(false);
Packit df99a1
	 GP<DataPool> data=file->get_djvu_data(false);
Packit df99a1
	 for(pos=files_list;pos;++pos)
Packit df99a1
	 {
Packit df99a1
	    GP<DjVuFile> f=files_list[pos];
Packit df99a1
	    if (f->contains_chunk("NDIR"))
Packit df99a1
	       data=DjVuFile::unlink_file(data, f->get_url().fname());
Packit df99a1
	 }
Packit df99a1
	 
Packit df99a1
	    // Finally add it to the document
Packit df99a1
	 GUTF8String name=file->get_url().fname();
Packit df99a1
	 GP<DjVmDir::File> file_rec=DjVmDir::File::create(
Packit df99a1
           name, name, name,
Packit df99a1
           page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE );
Packit df99a1
	 doc.insert_file(file_rec, data, -1);
Packit df99a1
Packit df99a1
	    // And repeat for all included files
Packit df99a1
	 for(pos=files_list;pos;++pos)
Packit df99a1
	    add_file_to_djvm(files_list[pos], false, doc, map);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
add_file_to_djvm(const GP<DjVuFile> & file, bool page,
Packit df99a1
		 DjVmDoc & doc, GMap<GURL, void *> & map, 
Packit df99a1
                 bool &needs_compression_flag, bool &can_compress_flag )
Packit df99a1
{
Packit df99a1
  if(!needs_compression_flag)
Packit df99a1
  {
Packit df99a1
    if(file->needs_compression())
Packit df99a1
    {
Packit df99a1
      can_compress_flag=true;
Packit df99a1
      needs_compression_flag=true;
Packit df99a1
    }else if(file->can_compress())
Packit df99a1
    {
Packit df99a1
      can_compress_flag=true;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  add_file_to_djvm(file,page,doc,map);
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
local_get_url_names(DjVuFile * f,const GMap<GURL, void *> & map,GMap<GURL,void *> &tmpmap)
Packit df99a1
{
Packit df99a1
   GURL url=f->get_url();
Packit df99a1
   if (!map.contains(url) && !tmpmap.contains(url))
Packit df99a1
   {
Packit df99a1
      tmpmap[url]=0;
Packit df99a1
      f->process_incl_chunks();
Packit df99a1
      GPList<DjVuFile> files_list=f->get_included_files(false);
Packit df99a1
      for(GPosition pos=files_list;pos;++pos)
Packit df99a1
         local_get_url_names(files_list[pos], map, tmpmap);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
local_get_url_names(DjVuFile * f, GMap<GURL, void *> & map)
Packit df99a1
{
Packit df99a1
   GMap<GURL,void *> tmpmap;
Packit df99a1
   local_get_url_names(f,map,tmpmap);
Packit df99a1
   for(GPosition pos=tmpmap;pos;++pos)
Packit df99a1
     map[tmpmap.key(pos)]=0;
Packit df99a1
}
Packit df99a1
Packit df99a1
GList<GURL>
Packit df99a1
DjVuDocument::get_url_names(void)
Packit df99a1
{
Packit df99a1
  check();
Packit df99a1
Packit df99a1
  GCriticalSectionLock lock(&url_names_lock);
Packit df99a1
  if(has_url_names)
Packit df99a1
    return url_names;
Packit df99a1
Packit df99a1
  GMap<GURL, void *> map;
Packit df99a1
  int i;
Packit df99a1
  if (doc_type==BUNDLED || doc_type==INDIRECT)
Packit df99a1
  {
Packit df99a1
    GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
Packit df99a1
    for(GPosition pos=files_list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GURL url=id_to_url(files_list[pos]->get_load_name());
Packit df99a1
      map[url]=0;
Packit df99a1
    }
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    int pages_num=get_pages_num();
Packit df99a1
    for(i=0;i
Packit df99a1
    {
Packit df99a1
      G_TRY
Packit df99a1
      {
Packit df99a1
        local_get_url_names(get_djvu_file(i), map);
Packit df99a1
      }
Packit df99a1
      G_CATCH(ex)
Packit df99a1
      {
Packit df99a1
        // Why is this try/catch block here?
Packit df99a1
        G_TRY { 
Packit df99a1
          get_portcaster()->notify_error(this, ex.get_cause()); 
Packit df99a1
          GUTF8String emsg = ERR_MSG("DjVuDocument.exclude_page") "\t" + GUTF8String(i+1);
Packit df99a1
          get_portcaster()->notify_error(this, emsg);
Packit df99a1
        }
Packit df99a1
        G_CATCH_ALL
Packit df99a1
        {
Packit df99a1
          G_RETHROW;
Packit df99a1
        }
Packit df99a1
        G_ENDCATCH;
Packit df99a1
      }
Packit df99a1
      G_ENDCATCH;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  for(GPosition j=map;j;++j)
Packit df99a1
  {
Packit df99a1
    if (map.key(j).is_local_file_url())
Packit df99a1
    {
Packit df99a1
      url_names.append(map.key(j));
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  has_url_names=true;
Packit df99a1
  return url_names;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVmDoc>
Packit df99a1
DjVuDocument::get_djvm_doc()
Packit df99a1
      // This function may block for data
Packit df99a1
{
Packit df99a1
   check();
Packit df99a1
   DEBUG_MSG("DjVuDocument::get_djvm_doc(): creating the DjVmDoc\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (!is_init_complete())
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocument.init_not_done") );
Packit df99a1
Packit df99a1
   GP<DjVmDoc> doc=DjVmDoc::create();
Packit df99a1
Packit df99a1
   if (doc_type==BUNDLED || doc_type==INDIRECT)
Packit df99a1
     {
Packit df99a1
       GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
Packit df99a1
       for(GPosition pos=files_list;pos;++pos)
Packit df99a1
         {
Packit df99a1
           GP<DjVmDir::File> f=new DjVmDir::File(*files_list[pos]);
Packit df99a1
           GP<DjVuFile> file=url_to_file(id_to_url(f->get_load_name()));
Packit df99a1
           GP<DataPool> data;
Packit df99a1
           if (file->is_modified()) 
Packit df99a1
             data=file->get_djvu_data(false);
Packit df99a1
           else 
Packit df99a1
             data=file->get_init_data_pool();
Packit df99a1
           doc->insert_file(f, data);
Packit df99a1
         }
Packit df99a1
       if (djvm_nav)
Packit df99a1
         doc->set_djvm_nav(djvm_nav);
Packit df99a1
     } 
Packit df99a1
   else if (doc_type==SINGLE_PAGE)
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("Creating: djvm for a single page document.\n");
Packit df99a1
       GMap<GURL, void *> map_add;
Packit df99a1
       GP<DjVuFile> file=get_djvu_file(0);
Packit df99a1
       add_file_to_djvm(file, true, *doc, map_add,
Packit df99a1
                        needs_compression_flag,can_compress_flag);
Packit df99a1
     } 
Packit df99a1
   else
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("Converting: the document is in an old format.\n");
Packit df99a1
       GMap<GURL, void *> map_add;
Packit df99a1
       if(recover_errors == ABORT)
Packit df99a1
         {
Packit df99a1
           for(int page_num=0;page_num<ndir->get_pages_num();page_num++)
Packit df99a1
             {
Packit df99a1
               GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num));
Packit df99a1
               add_file_to_djvm(file, true, *doc, map_add,
Packit df99a1
                                needs_compression_flag,can_compress_flag);
Packit df99a1
             }
Packit df99a1
         }
Packit df99a1
       else
Packit df99a1
         {
Packit df99a1
           for(int page_num=0;page_num<ndir->get_pages_num();page_num++)
Packit df99a1
             {
Packit df99a1
               G_TRY
Packit df99a1
                 {
Packit df99a1
                   GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num));
Packit df99a1
                   add_file_to_djvm(file, true, *doc, map_add,
Packit df99a1
                                    needs_compression_flag,can_compress_flag);
Packit df99a1
                 }
Packit df99a1
               G_CATCH(ex)
Packit df99a1
                 {
Packit df99a1
                   G_TRY { 
Packit df99a1
                     get_portcaster()->notify_error(this, ex.get_cause());
Packit df99a1
                     GUTF8String emsg = ERR_MSG("DjVuDocument.skip_page") "\t" 
Packit df99a1
                                      + GUTF8String(page_num+1);
Packit df99a1
                     get_portcaster()->notify_error(this, emsg);
Packit df99a1
                   }
Packit df99a1
                   G_CATCH_ALL
Packit df99a1
                     {
Packit df99a1
                       G_RETHROW;
Packit df99a1
                     }
Packit df99a1
                   G_ENDCATCH;
Packit df99a1
                 }
Packit df99a1
               G_ENDCATCH;
Packit df99a1
             }
Packit df99a1
         }
Packit df99a1
     }
Packit df99a1
   return doc;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::write( const GP<ByteStream> &gstr,
Packit df99a1
  const GMap<GUTF8String,void *> &reserved)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  get_djvm_doc()->write(gstr,reserved); 
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::write(const GP<ByteStream> &gstr, bool force_djvm)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
  GP<DjVmDoc> doc=get_djvm_doc();
Packit df99a1
  GP<DjVmDir> dir=doc->get_djvm_dir();
Packit df99a1
Packit df99a1
  bool singlepage = (dir->get_files_num()==1 && !djvm_nav && !force_djvm);
Packit df99a1
  if (singlepage)
Packit df99a1
  {
Packit df99a1
    // maybe save as single page
Packit df99a1
    DjVmDir::File *file = dir->page_to_file(0);
Packit df99a1
    if (file->get_title() != file->get_load_name())
Packit df99a1
      singlepage = false;
Packit df99a1
  }
Packit df99a1
  if (! singlepage)
Packit df99a1
  {
Packit df99a1
    doc->write(gstr);
Packit df99a1
  }
Packit df99a1
  else
Packit df99a1
  {
Packit df99a1
    GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false);
Packit df99a1
    GP<DataPool> pool=doc->get_data(files_list[files_list]->get_load_name());
Packit df99a1
    GP<ByteStream> pool_str=pool->get_stream();
Packit df99a1
    ByteStream &str=*gstr;
Packit df99a1
    str.writall(octets,4);
Packit df99a1
    str.copy(*pool_str);
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::expand(const GURL &codebase, const GUTF8String &idx_name)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::expand(): codebase='" << codebase << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   GP<DjVmDoc> doc=get_djvm_doc();
Packit df99a1
   doc->expand(codebase, idx_name);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::save_as(const GURL &where, bool bundled)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocument::save_as(): where='" << where <<
Packit df99a1
	     "', bundled=" << bundled << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   if (needs_compression())
Packit df99a1
   { 
Packit df99a1
     if(!djvu_compress_codec)
Packit df99a1
     {
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocument.comp_codec") );
Packit df99a1
     }
Packit df99a1
     GP<ByteStream> gmbs=ByteStream::create();
Packit df99a1
     write(gmbs);
Packit df99a1
     ByteStream &mbs=*gmbs;
Packit df99a1
     mbs.flush();
Packit df99a1
     mbs.seek(0,SEEK_SET);
Packit df99a1
     (*djvu_compress_codec)(gmbs,where,bundled);
Packit df99a1
   }else if (bundled)
Packit df99a1
   {
Packit df99a1
      DataPool::load_file(where);
Packit df99a1
      write(ByteStream::create(where, "wb"));
Packit df99a1
   } else 
Packit df99a1
   {
Packit df99a1
     expand(where.base(), where.fname());
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
static const char prolog[]="\n\n<DjVuXML>\n<HEAD>";
Packit df99a1
static const char start_xml[]="</HEAD>\n<BODY>\n";
Packit df99a1
static const char end_xml[]="</BODY>\n</DjVuXML>\n";
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocument::writeDjVuXML(const GP<ByteStream> &gstr_out,
Packit df99a1
                           int flags, int page) const
Packit df99a1
{
Packit df99a1
  ByteStream &str_out=*gstr_out;
Packit df99a1
  str_out.writestring(
Packit df99a1
    prolog+get_init_url().get_string().toEscaped()+start_xml);
Packit df99a1
  const int pages=wait_get_pages_num();
Packit df99a1
  int pstart = (page < 0) ? 0 : page;
Packit df99a1
  int pend = (page < 0) ? pages : page+1;
Packit df99a1
  for(int page_num=pstart; page_num
Packit df99a1
  {
Packit df99a1
    const GP<DjVuImage> dimg(get_page(page_num,true));
Packit df99a1
    if(!dimg)
Packit df99a1
    {
Packit df99a1
      G_THROW( ERR_MSG("DjVuToText.decode_failed") );
Packit df99a1
    }
Packit df99a1
    dimg->writeXML(str_out,get_init_url(),flags);
Packit df99a1
  }
Packit df99a1
  str_out.writestring(GUTF8String(end_xml));
Packit df99a1
}
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