Blame libdjvu/DjVuDocEditor.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 "DjVuDocEditor.h"
Packit df99a1
#include "DjVuImage.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "DataPool.h"
Packit df99a1
#include "IW44Image.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "GURL.h"
Packit df99a1
#include "DjVuAnno.h"
Packit df99a1
#include "GRect.h"
Packit df99a1
#include "DjVmNav.h"
Packit df99a1
Packit df99a1
#include "debug.h"
Packit df99a1
Packit df99a1
#include <ctype.h>
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
Packit df99a1
int        DjVuDocEditor::thumbnails_per_file=10;
Packit df99a1
Packit df99a1
// This is a structure for active files and DataPools. It may contain
Packit df99a1
// a DjVuFile, which is currently being used by someone (I check the list
Packit df99a1
// and get rid of hanging files from time to time) or a DataPool,
Packit df99a1
// which is "custom" with respect to the document (was modified or
Packit df99a1
// inserted), or both.
Packit df99a1
//
Packit df99a1
// DjVuFile is set to smth!=0 when it's created using url_to_file().
Packit df99a1
//          It's reset back to ZERO in clean_files_map() when
Packit df99a1
//	  it sees, that a given file is not used by anyone.
Packit df99a1
// DataPool is updated when a file is inserted
Packit df99a1
class DjVuDocEditor::File : public GPEnabled
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
  // 'pool' below may be non-zero only if it cannot be retrieved
Packit df99a1
  // by the DjVuDocument, that is it either corresponds to a
Packit df99a1
  // modified DjVuFile or it has been inserted. Otherwise it's ZERO
Packit df99a1
  // Once someone assigns a non-zero DataPool, it remains non-ZERO
Packit df99a1
  // (may be updated if the file gets modified) and may be reset
Packit df99a1
  // only by save() or save_as() functions.
Packit df99a1
  GP<DataPool>	pool;
Packit df99a1
Packit df99a1
  // If 'file' is non-zero, it means, that it's being used by someone
Packit df99a1
  // We check for unused files from time to time and ZERO them.
Packit df99a1
  // But before we do it, we may save the DataPool in the case if
Packit df99a1
  // file has been modified.
Packit df99a1
  GP<DjVuFile>	file;
Packit df99a1
};
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::check(void)
Packit df99a1
{
Packit df99a1
   if (!initialized) G_THROW( ERR_MSG("DjVuDocEditor.not_init") );
Packit df99a1
}
Packit df99a1
Packit df99a1
DjVuDocEditor::DjVuDocEditor(void)
Packit df99a1
{
Packit df99a1
   initialized=false;
Packit df99a1
   refresh_cb=0;
Packit df99a1
   refresh_cl_data=0;
Packit df99a1
}
Packit df99a1
Packit df99a1
DjVuDocEditor::~DjVuDocEditor(void)
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
   thumb_map.empty();
Packit df99a1
   DataPool::close_all();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::init(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::init() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // If you remove this check be sure to delete thumb_map
Packit df99a1
   if (initialized) G_THROW( ERR_MSG("DjVuDocEditor.init") );
Packit df99a1
Packit df99a1
   doc_url=GURL::Filename::UTF8("noname.djvu");
Packit df99a1
Packit df99a1
   const GP<DjVmDoc> doc(DjVmDoc::create());
Packit df99a1
   const GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
   doc->write(gstr);
Packit df99a1
   gstr->seek(0, SEEK_SET);
Packit df99a1
   doc_pool=DataPool::create(gstr);
Packit df99a1
Packit df99a1
   orig_doc_type=UNKNOWN_TYPE;
Packit df99a1
   orig_doc_pages=0;
Packit df99a1
Packit df99a1
   initialized=true;
Packit df99a1
Packit df99a1
   DjVuDocument::init(doc_url, this);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::init(const GURL &url)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::init() called: url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // If you remove this check be sure to delete thumb_map
Packit df99a1
   if (initialized)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.init") );
Packit df99a1
Packit df99a1
      // First - create a temporary DjVuDocument and check its type
Packit df99a1
   doc_pool=DataPool::create(url);
Packit df99a1
   doc_url=url;
Packit df99a1
   const GP<DjVuDocument> tmp_doc(DjVuDocument::create_wait(doc_url,this));
Packit df99a1
   if (!tmp_doc->is_init_ok())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.open_fail") "\t" +url.get_string());
Packit df99a1
Packit df99a1
   orig_doc_type=tmp_doc->get_doc_type();
Packit df99a1
   orig_doc_pages=tmp_doc->get_pages_num();
Packit df99a1
   if (orig_doc_type==OLD_BUNDLED ||
Packit df99a1
       orig_doc_type==OLD_INDEXED ||
Packit df99a1
       orig_doc_type==SINGLE_PAGE)
Packit df99a1
   {
Packit df99a1
     // Suxx. I need to convert it now.
Packit df99a1
     GP<ByteStream> gstr = ByteStream::create();  // Convert in memory.
Packit df99a1
     tmp_doc->write(gstr, true);  // Force DJVM format
Packit df99a1
     gstr->seek(0);                     
Packit df99a1
     doc_pool=DataPool::create(gstr);
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // OK. Now doc_pool contains data of the document in one of the
Packit df99a1
      // new formats. It will be a lot easier to insert/delete pages now.
Packit df99a1
      // 'doc_url' below of course doesn't refer to the file with the converted
Packit df99a1
      // data, but we will take care of it by redirecting the request_data().
Packit df99a1
   initialized=true;
Packit df99a1
   DjVuDocument::init(doc_url, this);
Packit df99a1
Packit df99a1
      // Cool. Now extract the thumbnails...
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
   int pages_num=get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
   {
Packit df99a1
	 // Call DjVuDocument::get_thumbnail() here to bypass logic
Packit df99a1
	 // of DjVuDocEditor::get_thumbnail(). init() is the only safe
Packit df99a1
	 // place where we can still call DjVuDocument::get_thumbnail();
Packit df99a1
      const GP<DataPool> pool(DjVuDocument::get_thumbnail(page_num, true));
Packit df99a1
      if (pool)
Packit df99a1
      {
Packit df99a1
        thumb_map[page_to_id(page_num)]=pool;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
      // And remove then from DjVmDir so that DjVuDocument
Packit df99a1
      // does not try to use them
Packit df99a1
   unfile_thumbnails();
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuDocEditor::request_data(const DjVuPort * source, const GURL & url)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::request_data(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // Check if we have either original data or converted (to new format),
Packit df99a1
      // if all the story is about the DjVuDocument's data
Packit df99a1
   if (url==doc_url)
Packit df99a1
     return doc_pool;
Packit df99a1
Packit df99a1
      // Now see if we have any file matching the url
Packit df99a1
   const GP<DjVmDir::File> frec(djvm_dir->name_to_file(url.fname()));
Packit df99a1
   if (frec)
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lock(&files_lock);
Packit df99a1
      GPosition pos;
Packit df99a1
      if (files_map.contains(frec->get_load_name(), pos))
Packit df99a1
      {
Packit df99a1
         const GP<File> f(files_map[pos]);
Packit df99a1
         if (f->file && f->file->get_init_data_pool())
Packit df99a1
            return f->file->get_init_data_pool();// Favor DjVuFile's knowledge
Packit df99a1
         else if (f->pool) return f->pool;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Finally let DjVuDocument cope with it. It may be a connected DataPool
Packit df99a1
      // for a BUNDLED format. Or it may be a file. Anyway, it was not
Packit df99a1
      // manually included, so it should be in the document.
Packit df99a1
   const GP<DataPool> pool(DjVuDocument::request_data(source, url));
Packit df99a1
Packit df99a1
      // We do NOT update the 'File' structure, because our rule is that
Packit df99a1
      // we keep a separate copy of DataPool in 'File' only if it cannot
Packit df99a1
      // be retrieved from DjVuDocument (like it has been "inserted" or
Packit df99a1
      // corresponds to a modified file).
Packit df99a1
   return pool;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::clean_files_map(void)
Packit df99a1
      // Will go thru the map of files looking for unreferenced
Packit df99a1
      // files or records w/o DjVuFile and DataPool.
Packit df99a1
      // These will be modified and/or removed.
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::clean_files_map() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock(&files_lock);
Packit df99a1
Packit df99a1
      // See if there are too old items in the "cache", which are
Packit df99a1
      // not referenced by anyone. If the corresponding DjVuFile has been
Packit df99a1
      // modified, obtain the new data and replace the 'pool'. Clear the
Packit df99a1
      // DjVuFile anyway. If both DataPool and DjVuFile are zero, remove
Packit df99a1
      // the entry.
Packit df99a1
   for(GPosition pos=files_map;pos;)
Packit df99a1
   {
Packit df99a1
      const GP<File> f(files_map[pos]);
Packit df99a1
      if (f->file && f->file->get_count()==1)
Packit df99a1
      {
Packit df99a1
         DEBUG_MSG("ZEROing file '" << f->file->get_url() << "'\n");
Packit df99a1
         if (f->file->is_modified())
Packit df99a1
            f->pool=f->file->get_djvu_data(false);
Packit df99a1
         f->file=0;
Packit df99a1
      }
Packit df99a1
      if (!f->file && !f->pool)
Packit df99a1
      {
Packit df99a1
         DEBUG_MSG("Removing record '" << files_map.key(pos) << "'\n");
Packit df99a1
         GPosition this_pos=pos;
Packit df99a1
         ++pos;
Packit df99a1
         files_map.del(this_pos);
Packit df99a1
      } else ++pos;
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocEditor::url_to_file(const GURL & url, bool dont_create) const
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::url_to_file(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // Check if have a DjVuFile with this url cached (created before
Packit df99a1
      // and either still active or left because it has been modified)
Packit df99a1
   GP<DjVmDir::File> frec;
Packit df99a1
   if((const DjVmDir *)djvm_dir)
Packit df99a1
     frec=djvm_dir->name_to_file(url.fname());
Packit df99a1
   if (frec)
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock));
Packit df99a1
      GPosition pos;
Packit df99a1
      if (files_map.contains(frec->get_load_name(), pos))
Packit df99a1
      {
Packit df99a1
         const GP<File> f(files_map[pos]);
Packit df99a1
         if (f->file)
Packit df99a1
           return f->file;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
   const_cast<DjVuDocEditor *>(this)->clean_files_map();
Packit df99a1
Packit df99a1
      // We don't have the file cached. Let DjVuDocument create the file.
Packit df99a1
   const GP<DjVuFile> file(DjVuDocument::url_to_file(url, dont_create));
Packit df99a1
Packit df99a1
      // And add it to our private "cache"
Packit df99a1
   if (file && frec)
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock));
Packit df99a1
      GPosition pos;
Packit df99a1
      if (files_map.contains(frec->get_load_name(), pos))
Packit df99a1
      {
Packit df99a1
         files_map[frec->get_load_name()]->file=file;
Packit df99a1
      }else
Packit df99a1
      {
Packit df99a1
         const GP<File> f(new File());
Packit df99a1
         f->file=file;
Packit df99a1
         const_cast<DjVuDocEditor *>(this)->files_map[frec->get_load_name()]=f;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
   return file;
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuDocEditor::page_to_id(int page_num) const
Packit df99a1
{
Packit df99a1
   if (page_num<0 || page_num>=get_pages_num())
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num));
Packit df99a1
   const GP<DjVmDir::File> f(djvm_dir->page_to_file(page_num));
Packit df99a1
   if (! f)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num));
Packit df99a1
Packit df99a1
   return f->get_load_name();
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuDocEditor::find_unique_id(GUTF8String id)
Packit df99a1
{
Packit df99a1
  const GP<DjVmDir> dir(get_djvm_dir());
Packit df99a1
Packit df99a1
  GUTF8String base, ext;
Packit df99a1
  const int dot=id.rsearch('.');
Packit df99a1
  if(dot >= 0)
Packit df99a1
  {
Packit df99a1
    base=id.substr(0,dot);
Packit df99a1
    ext=id.substr(dot+1,(unsigned int)-1);
Packit df99a1
  }else
Packit df99a1
  {
Packit df99a1
    base=id;
Packit df99a1
  }
Packit df99a1
Packit df99a1
  int cnt=0;
Packit df99a1
  while (!(!dir->id_to_file(id) &&
Packit df99a1
           !dir->name_to_file(id) &&
Packit df99a1
           !dir->title_to_file(id)))
Packit df99a1
  {
Packit df99a1
     cnt++;
Packit df99a1
     id=base+"_"+GUTF8String(cnt);
Packit df99a1
     if (ext.length())
Packit df99a1
       id+="."+ext;
Packit df99a1
  }
Packit df99a1
  return id;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuDocEditor::strip_incl_chunks(const GP<DataPool> & pool_in)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::strip_incl_chunks() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   const GP<IFFByteStream> giff_in(
Packit df99a1
     IFFByteStream::create(pool_in->get_stream()));
Packit df99a1
Packit df99a1
   const GP<ByteStream> gbs_out(ByteStream::create());
Packit df99a1
   const GP<IFFByteStream> giff_out(IFFByteStream::create(gbs_out));
Packit df99a1
Packit df99a1
   IFFByteStream &iff_in=*giff_in;
Packit df99a1
   IFFByteStream &iff_out=*giff_out;
Packit df99a1
Packit df99a1
   bool have_incl=false;
Packit df99a1
   int chksize;
Packit df99a1
   GUTF8String chkid;
Packit df99a1
   if (iff_in.get_chunk(chkid))
Packit df99a1
   {
Packit df99a1
      iff_out.put_chunk(chkid);
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
           have_incl=true;
Packit df99a1
         }
Packit df99a1
         iff_in.close_chunk();
Packit df99a1
      }
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
   }
Packit df99a1
Packit df99a1
   if (have_incl)
Packit df99a1
   {
Packit df99a1
      gbs_out->seek(0,SEEK_SET);
Packit df99a1
      return DataPool::create(gbs_out);
Packit df99a1
   } else return pool_in;
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
DjVuDocEditor::insert_file(const GURL &file_url, const GUTF8String &parent_id,
Packit df99a1
                           int chunk_num, DjVuPort *source)
Packit df99a1
      // Will open the 'file_name' and insert it into an existing DjVuFile
Packit df99a1
      // with ID 'parent_id'. Will insert the INCL chunk at position chunk_num
Packit df99a1
      // Will NOT process ANY files included into the file being inserted.
Packit df99a1
      // Moreover it will strip out any INCL chunks in that file...
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::insert_file(): fname='" << file_url <<
Packit df99a1
             "', parent_id='" << parent_id << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   const GP<DjVmDir> dir(get_djvm_dir());
Packit df99a1
Packit df99a1
   if(!source)
Packit df99a1
     source=this;
Packit df99a1
      // Create DataPool and see if the file exists
Packit df99a1
   GP<DataPool> file_pool;
Packit df99a1
   if(file_url.is_empty()||file_url.is_local_file_url())
Packit df99a1
   {
Packit df99a1
     file_pool=DataPool::create(file_url);
Packit df99a1
   }else
Packit df99a1
   {
Packit df99a1
     file_pool=source->request_data(source, file_url);
Packit df99a1
     if(source != this)
Packit df99a1
     {
Packit df99a1
       file_pool=DataPool::create(file_pool->get_stream());
Packit df99a1
     }
Packit df99a1
   }
Packit df99a1
   if(file_pool && file_url && DjVuDocument::djvu_import_codec)
Packit df99a1
   {
Packit df99a1
     (*DjVuDocument::djvu_import_codec)(file_pool,file_url,needs_compression_flag,can_compress_flag);
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Strip any INCL chunks
Packit df99a1
   file_pool=strip_incl_chunks(file_pool);
Packit df99a1
Packit df99a1
      // Check if parent ID is valid
Packit df99a1
   GP<DjVmDir::File> parent_frec(dir->id_to_file(parent_id));
Packit df99a1
   if (!parent_frec)
Packit df99a1
     parent_frec=dir->name_to_file(parent_id);
Packit df99a1
   if (!parent_frec)
Packit df99a1
     parent_frec=dir->title_to_file(parent_id);
Packit df99a1
   if (!parent_frec)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t" +parent_id);
Packit df99a1
   const GP<DjVuFile> parent_file(get_djvu_file(parent_id));
Packit df99a1
   if (!parent_file)
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.create_fail") "\t"+parent_id);
Packit df99a1
Packit df99a1
      // Now obtain ID for the new file
Packit df99a1
   const GUTF8String id(find_unique_id(file_url.fname()));
Packit df99a1
Packit df99a1
      // Add it into the directory
Packit df99a1
   const GP<DjVmDir::File> frec(
Packit df99a1
     DjVmDir::File::create(id, id, id, DjVmDir::File::INCLUDE));
Packit df99a1
   int pos=dir->get_file_pos(parent_frec);
Packit df99a1
   if (pos>=0)
Packit df99a1
     ++pos;
Packit df99a1
   dir->insert_file(frec, pos);
Packit df99a1
Packit df99a1
      // Add it to our "cache"
Packit df99a1
   {
Packit df99a1
      const GP<File> f(new File);
Packit df99a1
      f->pool=file_pool;
Packit df99a1
      GCriticalSectionLock lock(&files_lock);
Packit df99a1
      files_map[id]=f;
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // And insert it into the parent DjVuFile
Packit df99a1
   parent_file->insert_file(id, chunk_num);
Packit df99a1
Packit df99a1
   return id;
Packit df99a1
}
Packit df99a1
Packit df99a1
      // First it will insert the 'file_url' at position 'file_pos'.
Packit df99a1
      //
Packit df99a1
      // Then it will process all the INCL chunks in the file and try to do
Packit df99a1
      // the same thing with the included files. If insertion of an included
Packit df99a1
      // file fails, it will proceed with other INCL chunks until it does
Packit df99a1
      // them all. In the very end we will throw exception to let the caller
Packit df99a1
      // know about problems with included files.
Packit df99a1
      //
Packit df99a1
      // If the name of a file being inserted conflicts with some other
Packit df99a1
      // name, which has been in DjVmDir prior to call to this function,
Packit df99a1
      // it will be modified. name2id is the translation table to
Packit df99a1
      // keep track of these modifications.
Packit df99a1
      //
Packit df99a1
      // Also, if a name is in name2id, we will not insert that file again.
Packit df99a1
      //
Packit df99a1
      // Will return TRUE if the file has been successfully inserted.
Packit df99a1
      // FALSE, if the file contains NDIR chunk and has been skipped.
Packit df99a1
bool
Packit df99a1
DjVuDocEditor::insert_file(const GURL &file_url, bool is_page,
Packit df99a1
  int & file_pos, GMap<GUTF8String, GUTF8String> & name2id,
Packit df99a1
  DjVuPort *source)
Packit df99a1
{
Packit df99a1
Packit df99a1
  DEBUG_MSG("DjVuDocEditor::insert_file(): file_url='" << file_url <<
Packit df99a1
             "', is_page='" << is_page << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  if (refresh_cb)
Packit df99a1
    refresh_cb(refresh_cl_data);
Packit df99a1
Packit df99a1
Packit df99a1
      // We do not want to insert the same file twice (important when
Packit df99a1
      // we insert a group of files at the same time using insert_group())
Packit df99a1
      // So we check if we already did that and return if so.
Packit df99a1
  if (name2id.contains(file_url.fname()))
Packit df99a1
    return true;
Packit df99a1
Packit df99a1
  if(!source)
Packit df99a1
    source=this;
Packit df99a1
Packit df99a1
  GP<DataPool> file_pool;
Packit df99a1
  if(file_url.is_empty()||file_url.is_local_file_url())
Packit df99a1
  {
Packit df99a1
    file_pool=DataPool::create(file_url);
Packit df99a1
  }
Packit df99a1
  else
Packit df99a1
  {
Packit df99a1
    file_pool=source->request_data(source, file_url);
Packit df99a1
    if(source != this)
Packit df99a1
    {
Packit df99a1
      file_pool=DataPool::create(file_pool->get_stream());
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
       // Create DataPool and see if the file exists
Packit df99a1
  if(file_pool && !file_url.is_empty() && DjVuDocument::djvu_import_codec)
Packit df99a1
  {
Packit df99a1
      (*DjVuDocument::djvu_import_codec)(file_pool,file_url,
Packit df99a1
                                         needs_compression_flag,
Packit df99a1
                                         can_compress_flag);
Packit df99a1
  }
Packit df99a1
Packit df99a1
  // Oh. It does exist... Check that it has IFF structure
Packit df99a1
  {
Packit df99a1
    const GP<IFFByteStream> giff(
Packit df99a1
       IFFByteStream::create(file_pool->get_stream()));
Packit df99a1
    IFFByteStream &iff=*giff;
Packit df99a1
    GUTF8String chkid;
Packit df99a1
    iff.get_chunk(chkid);
Packit df99a1
    if (chkid!="FORM:DJVI" && chkid!="FORM:DJVU" &&
Packit df99a1
        chkid!="FORM:BM44" && chkid!="FORM:PM44")
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.not_1_page") "\t"
Packit df99a1
               + file_url.get_string());
Packit df99a1
    // Wonderful. It's even a DjVu file. Scan for NDIR chunks.
Packit df99a1
    // If NDIR chunk is found, ignore the file
Packit df99a1
    while(iff.get_chunk(chkid))
Packit df99a1
      {
Packit df99a1
        if (chkid=="NDIR")
Packit df99a1
          return false;
Packit df99a1
        iff.close_chunk();
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
  return insert_file(file_pool,file_url,is_page,file_pos,name2id,source);
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuDocEditor::insert_file(const GP<DataPool> &file_pool,
Packit df99a1
  const GURL &file_url, bool is_page,
Packit df99a1
  int & file_pos, GMap<GUTF8String, GUTF8String> & name2id,
Packit df99a1
  DjVuPort *source)
Packit df99a1
{
Packit df99a1
  GUTF8String errors;
Packit df99a1
  if(file_pool)
Packit df99a1
  {
Packit df99a1
    const GP<DjVmDir> dir(get_djvm_dir());
Packit df99a1
    G_TRY
Packit df99a1
    {
Packit df99a1
         // Now get a unique name for this file.
Packit df99a1
         // Check the name2id first...
Packit df99a1
      const GUTF8String name=file_url.fname();
Packit df99a1
      GUTF8String id;
Packit df99a1
      if (name2id.contains(name))
Packit df99a1
      {
Packit df99a1
        id=name2id[name];
Packit df99a1
      }else
Packit df99a1
      {
Packit df99a1
           // Check to see if this page exists with a different name.
Packit df99a1
        if(!is_page)
Packit df99a1
        {
Packit df99a1
          GPList<DjVmDir::File> list(dir->get_files_list());
Packit df99a1
          for(GPosition pos=list;pos;++pos)
Packit df99a1
          {
Packit df99a1
            DEBUG_MSG("include " << list[pos]->is_include() 
Packit df99a1
                      << " size=" << list[pos]->size << " length=" 
Packit df99a1
                      << file_pool->get_length() << "\n");
Packit df99a1
            if(list[pos]->is_include() 
Packit df99a1
               && (!list[pos]->size 
Packit df99a1
                   || (list[pos]->size == file_pool->get_length())))
Packit df99a1
            {
Packit df99a1
              id=list[pos]->get_load_name();
Packit df99a1
              GP<DjVuFile> file(get_djvu_file(id,false));
Packit df99a1
              const GP<DataPool> pool(file->get_djvu_data(false));
Packit df99a1
              if(file_pool->simple_compare(*pool))
Packit df99a1
              {
Packit df99a1
                // The files are the same, so just store the alias.
Packit df99a1
                name2id[name]=id;
Packit df99a1
              }
Packit df99a1
              const GP<IFFByteStream> giff_old(IFFByteStream::create(pool->get_stream()));
Packit df99a1
              const GP<IFFByteStream> giff_new(IFFByteStream::create(file_pool->get_stream()));
Packit df99a1
              file=0;
Packit df99a1
              if(giff_old->compare(*giff_new))
Packit df99a1
              {
Packit df99a1
                // The files are the same, so just store the alias.
Packit df99a1
                name2id[name]=id;
Packit df99a1
                return true;
Packit df99a1
              }
Packit df99a1
            } 
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        // Otherwise create a new unique ID and remember the translation
Packit df99a1
        id=find_unique_id(name);
Packit df99a1
        name2id[name]=id;
Packit df99a1
      }
Packit df99a1
Packit df99a1
         // Good. Before we continue with the included files we want to
Packit df99a1
         // complete insertion of this one. Notice, that insertion of
Packit df99a1
         // children may fail, in which case we will have to modify
Packit df99a1
         // data for this file to get rid of invalid INCL
Packit df99a1
Packit df99a1
         // Create a file record with the chosen ID
Packit df99a1
      const GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id,
Packit df99a1
        is_page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE ));
Packit df99a1
Packit df99a1
         // And insert it into the directory
Packit df99a1
      file_pos=dir->insert_file(file, file_pos);
Packit df99a1
Packit df99a1
         // And add the File record (containing the file URL and DataPool)
Packit df99a1
      {
Packit df99a1
         const GP<File> f(new File);
Packit df99a1
         f->pool=file_pool;
Packit df99a1
         GCriticalSectionLock lock(&files_lock);
Packit df99a1
         files_map[id]=f;
Packit df99a1
      }
Packit df99a1
Packit df99a1
         // The file has been added. If it doesn't include anything else,
Packit df99a1
         // that will be enough. Otherwise repeat what we just did for every
Packit df99a1
         // included child. Don't forget to modify the contents of INCL
Packit df99a1
         // chunks due to name2id translation.
Packit df99a1
         // We also want to include here our file with shared annotations,
Packit df99a1
         // if it exists.
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      const GP<IFFByteStream> giff_in(
Packit df99a1
        IFFByteStream::create(file_pool->get_stream()));
Packit df99a1
      IFFByteStream &iff_in=*giff_in;
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<DjVmDir::File> shared_frec(djvm_dir->get_shared_anno_file());
Packit df99a1
Packit df99a1
      iff_in.get_chunk(chkid);
Packit df99a1
      iff_out.put_chunk(chkid);
Packit df99a1
      while(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_in.close_chunk();
Packit df99a1
            iff_out.close_chunk();
Packit df99a1
            if (shared_frec && chkid=="INFO")
Packit df99a1
            {
Packit df99a1
               iff_out.put_chunk("INCL");
Packit df99a1
               iff_out.get_bytestream()->writestring(shared_frec->get_load_name());
Packit df99a1
               iff_out.close_chunk();
Packit df99a1
            }
Packit df99a1
         } else
Packit df99a1
         {
Packit df99a1
            GUTF8String name;
Packit df99a1
            char buffer[1024];
Packit df99a1
            int length;
Packit df99a1
            while((length=iff_in.read(buffer, 1024)))
Packit df99a1
               name+=GUTF8String(buffer, length);
Packit df99a1
            while(isspace(name[0]))
Packit df99a1
            {
Packit df99a1
              name=name.substr(1,(unsigned int)-1);
Packit df99a1
            }
Packit df99a1
            while(isspace(name[(int)name.length()-1]))
Packit df99a1
            {
Packit df99a1
              name.setat(name.length()-1, 0);
Packit df99a1
            }
Packit df99a1
            const GURL::UTF8 full_url(name,file_url.base());
Packit df99a1
            iff_in.close_chunk();
Packit df99a1
Packit df99a1
            G_TRY {
Packit df99a1
               if (insert_file(full_url, false, file_pos, name2id, source))
Packit df99a1
               {
Packit df99a1
                     // If the child file has been inserted (doesn't
Packit df99a1
                     // contain NDIR chunk), add INCL chunk.
Packit df99a1
                  GUTF8String id=name2id[name];
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
            } G_CATCH(exc) {
Packit df99a1
                  // Should an error occur, we move on. INCL chunk will
Packit df99a1
                  // not be copied.
Packit df99a1
               if (errors.length())
Packit df99a1
                 errors+="\n\n";
Packit df99a1
               errors+=exc.get_cause();
Packit df99a1
            } G_ENDCATCH;
Packit df99a1
         }
Packit df99a1
      } // while(iff_in.get_chunk(chkid))
Packit df99a1
      iff_out.close_chunk();
Packit df99a1
Packit df99a1
      // Increment the file_pos past the page inserted.
Packit df99a1
      if (file_pos>=0) file_pos++;
Packit df99a1
Packit df99a1
         // We have just inserted every included file. We may have modified
Packit df99a1
         // contents of the INCL chunks. So we need to update the DataPool...
Packit df99a1
      gstr_out->seek(0);
Packit df99a1
      const GP<DataPool> new_file_pool(DataPool::create(gstr_out));
Packit df99a1
      {
Packit df99a1
            // It's important that we replace the pool here anyway.
Packit df99a1
            // By doing this we load the file into memory. And this is
Packit df99a1
            // exactly what insert_group() wants us to do because
Packit df99a1
            // it creates temporary files.
Packit df99a1
         GCriticalSectionLock lock(&files_lock);
Packit df99a1
         files_map[id]->pool=new_file_pool;
Packit df99a1
      }
Packit df99a1
    } G_CATCH(exc) {
Packit df99a1
      if (errors.length())
Packit df99a1
        errors+="\n\n";
Packit df99a1
      errors+=exc.get_cause();
Packit df99a1
      G_THROW(errors);
Packit df99a1
    } G_ENDCATCH;
Packit df99a1
Packit df99a1
      // The only place where we intercept exceptions is when we process
Packit df99a1
      // included files. We want to process all of them even if we failed to
Packit df99a1
      // process one. But here we need to let the exception propagate...
Packit df99a1
    if (errors.length())
Packit df99a1
      G_THROW(errors);
Packit df99a1
Packit df99a1
    return true;
Packit df99a1
  }
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::insert_group(const GList<GURL> & file_urls, int page_num,
Packit df99a1
                             void (* _refresh_cb)(void *), void * _cl_data)
Packit df99a1
      // The function will insert every file from the list at position
Packit df99a1
      // corresponding to page_num. If page_num is negative, concatenation
Packit df99a1
      // will occur. Included files will be processed as well
Packit df99a1
{
Packit df99a1
  refresh_cb=_refresh_cb;
Packit df99a1
  refresh_cl_data=_cl_data;
Packit df99a1
Packit df99a1
  G_TRY
Packit df99a1
  {
Packit df99a1
Packit df99a1
     // First translate the page_num to file_pos.
Packit df99a1
    const GP<DjVmDir> dir(get_djvm_dir());
Packit df99a1
    int file_pos;
Packit df99a1
    if (page_num<0 || page_num>=dir->get_pages_num())
Packit df99a1
    {
Packit df99a1
      file_pos=-1;
Packit df99a1
    }
Packit df99a1
    else
Packit df99a1
    {
Packit df99a1
      file_pos=dir->get_page_pos(page_num);
Packit df99a1
    }
Packit df99a1
Packit df99a1
       // Now call the insert_file() for every page. We will remember the
Packit df99a1
       // name2id translation table. Thus insert_file() will remember IDs
Packit df99a1
       // it assigned to shared files
Packit df99a1
    GMap<GUTF8String, GUTF8String> name2id;
Packit df99a1
Packit df99a1
    GUTF8String errors;
Packit df99a1
    for(GPosition pos=file_urls;pos;++pos)
Packit df99a1
    {
Packit df99a1
      const GURL &furl=file_urls[pos];
Packit df99a1
      DEBUG_MSG( "Inserting file '" << furl << "'\n" );
Packit df99a1
      G_TRY
Packit df99a1
      {
Packit df99a1
               // Check if it's a multipage document...
Packit df99a1
        GP<DataPool> xdata_pool(DataPool::create(furl));
Packit df99a1
        if(xdata_pool && furl.is_valid()
Packit df99a1
           && furl.is_local_file_url() && DjVuDocument::djvu_import_codec)
Packit df99a1
        {
Packit df99a1
          (*DjVuDocument::djvu_import_codec)(xdata_pool,furl,
Packit df99a1
                                             needs_compression_flag,
Packit df99a1
                                             can_compress_flag);
Packit df99a1
        }
Packit df99a1
        GUTF8String chkid;
Packit df99a1
        IFFByteStream::create(xdata_pool->get_stream())->get_chunk(chkid);
Packit df99a1
        if (name2id.contains(furl.fname())||(chkid=="FORM:DJVM"))
Packit df99a1
        {
Packit df99a1
          GMap<GUTF8String,void *> map;
Packit df99a1
          map_ids(map);
Packit df99a1
          DEBUG_MSG("Read DjVuDocument furl='" << furl << "'\n");
Packit df99a1
          GP<ByteStream> gbs(ByteStream::create());
Packit df99a1
          GP<DjVuDocument> doca(DjVuDocument::create_noinit());
Packit df99a1
          doca->set_verbose_eof(verbose_eof);
Packit df99a1
          doca->set_recover_errors(recover_errors);
Packit df99a1
          doca->init(furl /* ,this */ );
Packit df99a1
          doca->wait_for_complete_init();
Packit df99a1
          get_portcaster()->add_route(doca,this);
Packit df99a1
          DEBUG_MSG("Saving DjVuDocument url='" << furl << "' with unique names\n");
Packit df99a1
          doca->write(gbs,map);
Packit df99a1
          gbs->seek(0L);
Packit df99a1
          DEBUG_MSG("Loading unique names\n");
Packit df99a1
          GP<DjVuDocument> doc(DjVuDocument::create(gbs));
Packit df99a1
          doc->set_verbose_eof(verbose_eof);
Packit df99a1
          doc->set_recover_errors(recover_errors);
Packit df99a1
          doc->wait_for_complete_init();
Packit df99a1
          get_portcaster()->add_route(doc,this);
Packit df99a1
          gbs=0;
Packit df99a1
          DEBUG_MSG("Inserting pages\n");
Packit df99a1
          int pages_num=doc->get_pages_num();
Packit df99a1
          for(int page_num=0;page_num
Packit df99a1
          {
Packit df99a1
            const GURL url(doc->page_to_url(page_num));
Packit df99a1
            insert_file(url, true, file_pos, name2id, doc);
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
        else
Packit df99a1
        {
Packit df99a1
          insert_file(furl, true, file_pos, name2id, this);
Packit df99a1
        }
Packit df99a1
      } G_CATCH(exc)
Packit df99a1
      {
Packit df99a1
        if (errors.length())
Packit df99a1
        {
Packit df99a1
          errors+="\n\n";
Packit df99a1
        }
Packit df99a1
        errors+=exc.get_cause();
Packit df99a1
      }
Packit df99a1
      G_ENDCATCH;
Packit df99a1
    }
Packit df99a1
    if (errors.length())
Packit df99a1
    {
Packit df99a1
      G_THROW(errors);
Packit df99a1
    }
Packit df99a1
  } G_CATCH_ALL
Packit df99a1
  {
Packit df99a1
    refresh_cb=0;
Packit df99a1
    refresh_cl_data=0;
Packit df99a1
    G_RETHROW;
Packit df99a1
  } G_ENDCATCH;
Packit df99a1
  refresh_cb=0;
Packit df99a1
  refresh_cl_data=0;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::insert_page(const GURL &file_url, int page_num)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::insert_page(): furl='" << file_url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GList<GURL> list;
Packit df99a1
   list.append(file_url);
Packit df99a1
Packit df99a1
   insert_group(list, page_num);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::insert_page(GP<DataPool> & _file_pool,
Packit df99a1
			   const GURL & file_url, int page_num)
Packit df99a1
      // Use _file_pool as source of data, create a new DjVuFile
Packit df99a1
      // with name file_name, and insert it as page number page_num
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::insert_page(): pool size='" <<
Packit df99a1
	     _file_pool->get_size() << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   const GP<DjVmDir> dir(get_djvm_dir());
Packit df99a1
Packit df99a1
      // Strip any INCL chunks (we do not allow to insert hierarchies
Packit df99a1
      // using this function)
Packit df99a1
   const GP<DataPool> file_pool(strip_incl_chunks(_file_pool));
Packit df99a1
   
Packit df99a1
      // Now obtain ID for the new file
Packit df99a1
   const GUTF8String id(find_unique_id(file_url.fname()));
Packit df99a1
Packit df99a1
      // Add it into the directory
Packit df99a1
   const GP<DjVmDir::File> frec(DjVmDir::File::create(
Packit df99a1
     id, id, id, DjVmDir::File::PAGE));
Packit df99a1
   int pos=dir->get_page_pos(page_num);
Packit df99a1
   dir->insert_file(frec, pos);
Packit df99a1
Packit df99a1
      // Add it to our "cache"
Packit df99a1
   {
Packit df99a1
      GP<File> f=new File;
Packit df99a1
      f->pool=file_pool;
Packit df99a1
      GCriticalSectionLock lock(&files_lock);
Packit df99a1
      files_map[id]=f;
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::generate_ref_map(const GP<DjVuFile> & file,
Packit df99a1
				GMap<GUTF8String, void *> & ref_map,
Packit df99a1
				GMap<GURL, void *> & visit_map)
Packit df99a1
      // This private function is used to generate a list (implemented as map)
Packit df99a1
      // of files referencing the given file. To get list of all parents
Packit df99a1
      // for file with ID 'id' iterate map obtained as
Packit df99a1
      // *((GMap<GUTF8String, void *> *) ref_map[id])
Packit df99a1
{
Packit df99a1
   const GURL url=file->get_url();
Packit df99a1
   const GUTF8String id(djvm_dir->name_to_file(url.fname())->get_load_name());
Packit df99a1
   if (!visit_map.contains(url))
Packit df99a1
   {
Packit df99a1
      visit_map[url]=0;
Packit df99a1
Packit df99a1
      GPList<DjVuFile> files_list=file->get_included_files(false);
Packit df99a1
      for(GPosition pos=files_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
         GP<DjVuFile> child_file=files_list[pos];
Packit df99a1
            // First: add the current file to the list of parents for
Packit df99a1
            // the child being processed
Packit df99a1
         GURL child_url=child_file->get_url();
Packit df99a1
         const GUTF8String child_id(
Packit df99a1
           djvm_dir->name_to_file(child_url.fname())->get_load_name());
Packit df99a1
         GMap<GUTF8String, void *> * parents=0;
Packit df99a1
         if (ref_map.contains(child_id))
Packit df99a1
            parents=(GMap<GUTF8String, void *> *) ref_map[child_id];
Packit df99a1
         else
Packit df99a1
            ref_map[child_id]=parents=new GMap<GUTF8String, void *>();
Packit df99a1
         (*parents)[id]=0;
Packit df99a1
            // Second: go recursively
Packit df99a1
         generate_ref_map(child_file, ref_map, visit_map);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref,
Packit df99a1
                           GMap<GUTF8String, void *> & ref_map)
Packit df99a1
      // Private function, which will remove file with ID id.
Packit df99a1
      //
Packit df99a1
      // If will also remove all INCL chunks in parent files pointing
Packit df99a1
      // to this one
Packit df99a1
      //
Packit df99a1
      // Finally, if remove_unref is TRUE, we will go down the files
Packit df99a1
      // hierarchy removing every file, which becomes unreferenced.
Packit df99a1
      //
Packit df99a1
      // ref_map will be used to find out list of parents referencing
Packit df99a1
      // this file (required when removing INCL chunks)
Packit df99a1
{
Packit df99a1
      // First get rid of INCL chunks in parents
Packit df99a1
   GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[id];
Packit df99a1
   if (parents)
Packit df99a1
   {
Packit df99a1
      for(GPosition pos=*parents;pos;++pos)
Packit df99a1
      {
Packit df99a1
         const GUTF8String parent_id((*parents).key(pos));
Packit df99a1
         const GP<DjVuFile> parent(get_djvu_file(parent_id));
Packit df99a1
         if (parent)
Packit df99a1
           parent->unlink_file(id);
Packit df99a1
      }
Packit df99a1
      delete parents;
Packit df99a1
      parents=0;
Packit df99a1
      ref_map.del(id);
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // We will accumulate errors here.
Packit df99a1
   GUTF8String errors;
Packit df99a1
Packit df99a1
      // Now modify the ref_map and process children if necessary
Packit df99a1
   GP<DjVuFile> file=get_djvu_file(id);
Packit df99a1
   if (file)
Packit df99a1
   {
Packit df99a1
      G_TRY {
Packit df99a1
         GPList<DjVuFile> files_list=file->get_included_files(false);
Packit df99a1
         for(GPosition pos=files_list;pos;++pos)
Packit df99a1
         {
Packit df99a1
            GP<DjVuFile> child_file=files_list[pos];
Packit df99a1
            GURL child_url=child_file->get_url();
Packit df99a1
            const GUTF8String child_id(
Packit df99a1
              djvm_dir->name_to_file(child_url.fname())->get_load_name());
Packit df99a1
            GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[child_id];
Packit df99a1
            if (parents) parents->del(id);
Packit df99a1
Packit df99a1
            if (remove_unref && (!parents || !parents->size()))
Packit df99a1
               remove_file(child_id, remove_unref, ref_map);
Packit df99a1
         }
Packit df99a1
      } G_CATCH(exc) {
Packit df99a1
         if (errors.length()) errors+="\n\n";
Packit df99a1
         errors+=exc.get_cause();
Packit df99a1
      } G_ENDCATCH;
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Finally remove this file from the directory.
Packit df99a1
   djvm_dir->delete_file(id);
Packit df99a1
Packit df99a1
      // And get rid of its thumbnail, if any
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
   GPosition pos(thumb_map.contains(id));
Packit df99a1
   if (pos)
Packit df99a1
   {
Packit df99a1
     thumb_map.del(pos);
Packit df99a1
   }
Packit df99a1
   if (errors.length())
Packit df99a1
     G_THROW(errors);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::remove_file(): id='" << id << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (!djvm_dir->id_to_file(id))
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t"+id);
Packit df99a1
Packit df99a1
      // First generate a map of references (containing the list of parents
Packit df99a1
      // including this particular file. This will speed things up
Packit df99a1
      // significatly.
Packit df99a1
   GMap<GUTF8String, void *> ref_map;        // GMap<GUTF8String, GMap<GUTF8String, void *> *> in fact
Packit df99a1
   GMap<GURL, void *> visit_map;        // To avoid loops
Packit df99a1
Packit df99a1
   int pages_num=djvm_dir->get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
      generate_ref_map(get_djvu_file(page_num), ref_map, visit_map);
Packit df99a1
Packit df99a1
      // Now call the function, which will do the removal recursively
Packit df99a1
   remove_file(id, remove_unref, ref_map);
Packit df99a1
Packit df99a1
      // And clear the ref_map
Packit df99a1
   GPosition pos;
Packit df99a1
   while((pos=ref_map))
Packit df99a1
   {
Packit df99a1
      GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[pos];
Packit df99a1
      delete parents;
Packit df99a1
      ref_map.del(pos);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::remove_page(int page_num, bool remove_unref)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::remove_page(): page_num=" << page_num << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // Translate the page_num to ID
Packit df99a1
   GP<DjVmDir> djvm_dir=get_djvm_dir();
Packit df99a1
   if (page_num<0 || page_num>=djvm_dir->get_pages_num())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
Packit df99a1
Packit df99a1
      // And call general remove_file()
Packit df99a1
   remove_file(djvm_dir->page_to_file(page_num)->get_load_name(), remove_unref);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::remove_pages(const GList<int> & page_list, bool remove_unref)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::remove_pages() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // First we need to translate page numbers to IDs (they will
Packit df99a1
      // obviously be changing while we're removing pages one after another)
Packit df99a1
   GP<DjVmDir> djvm_dir=get_djvm_dir();
Packit df99a1
   GPosition pos ;
Packit df99a1
   if (djvm_dir)
Packit df99a1
   {
Packit df99a1
      GList<GUTF8String> id_list;
Packit df99a1
      for(pos=page_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
         GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
Packit df99a1
         if (frec)
Packit df99a1
            id_list.append(frec->get_load_name());
Packit df99a1
      }
Packit df99a1
Packit df99a1
      for(pos=id_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
         GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
Packit df99a1
         if (frec)
Packit df99a1
            remove_page(frec->get_page_num(), remove_unref);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::move_file(const GUTF8String &id, int & file_pos,
Packit df99a1
                         GMap<GUTF8String, void *> & map)
Packit df99a1
      // NOTE! file_pos here is the desired position in DjVmDir *after*
Packit df99a1
      // the record with ID 'id' is removed.
Packit df99a1
{
Packit df99a1
   if (!map.contains(id))
Packit df99a1
   {
Packit df99a1
      map[id]=0;
Packit df99a1
Packit df99a1
      GP<DjVmDir::File> file_rec=djvm_dir->id_to_file(id);
Packit df99a1
      if (file_rec)
Packit df99a1
      {
Packit df99a1
         file_rec=new DjVmDir::File(*file_rec);
Packit df99a1
         djvm_dir->delete_file(id);
Packit df99a1
         djvm_dir->insert_file(file_rec, file_pos);
Packit df99a1
Packit df99a1
         if (file_pos>=0)
Packit df99a1
         {
Packit df99a1
            file_pos++;
Packit df99a1
        
Packit df99a1
               // We care to move included files only if we do not append
Packit df99a1
               // This is because the only reason why we move included
Packit df99a1
               // files is to made them available sooner than they would
Packit df99a1
               // be available if we didn't move them. By appending files
Packit df99a1
               // we delay the moment when the data for the file becomes
Packit df99a1
               // available, of course.
Packit df99a1
            GP<DjVuFile> djvu_file=get_djvu_file(id);
Packit df99a1
            if (djvu_file)
Packit df99a1
            {
Packit df99a1
               GPList<DjVuFile> files_list=djvu_file->get_included_files(false);
Packit df99a1
               for(GPosition pos=files_list;pos;++pos)
Packit df99a1
               {
Packit df99a1
                  const GUTF8String name(files_list[pos]->get_url().fname());
Packit df99a1
                  GP<DjVmDir::File> child_frec=djvm_dir->name_to_file(name);
Packit df99a1
Packit df99a1
                     // If the child is positioned in DjVmDir AFTER the
Packit df99a1
                     // file being processed (position is file_pos or greater),
Packit df99a1
                     // move it to file_pos position
Packit df99a1
                  if (child_frec)
Packit df99a1
                     if (djvm_dir->get_file_pos(child_frec)>file_pos)
Packit df99a1
                        move_file(child_frec->get_load_name(), file_pos, map);
Packit df99a1
               }
Packit df99a1
            }
Packit df99a1
         }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::move_page(int page_num, int new_page_num)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocEditor::move_page(): page_num=" << page_num <<
Packit df99a1
	    ", new_page_num=" << new_page_num << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
  if (page_num==new_page_num) return;
Packit df99a1
Packit df99a1
  int pages_num=get_pages_num();
Packit df99a1
  if (page_num<0 || page_num>=pages_num)
Packit df99a1
    G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
Packit df99a1
Packit df99a1
  const GUTF8String id(page_to_id(page_num));
Packit df99a1
  int file_pos=-1;
Packit df99a1
  if (new_page_num>=0 && new_page_num
Packit df99a1
    {
Packit df99a1
      if (new_page_num>page_num)        // Moving toward the end
Packit df99a1
	{
Packit df99a1
	  if (new_page_num
Packit df99a1
	    file_pos=djvm_dir->get_page_pos(new_page_num+1)-1;
Packit df99a1
	}
Packit df99a1
      else
Packit df99a1
	file_pos=djvm_dir->get_page_pos(new_page_num);
Packit df99a1
    }
Packit df99a1
Packit df99a1
  GMap<GUTF8String, void *> map;
Packit df99a1
  move_file(id, file_pos, map);
Packit df99a1
}
Packit df99a1
Packit df99a1
#ifdef _WIN32_WCE_EMULATION         // Work around odd behavior under WCE Emulation
Packit df99a1
#define CALLINGCONVENTION __cdecl
Packit df99a1
#else
Packit df99a1
#define CALLINGCONVENTION  /* */
Packit df99a1
#endif
Packit df99a1
Packit df99a1
static int
Packit df99a1
CALLINGCONVENTION
Packit df99a1
cmp(const void * ptr1, const void * ptr2)
Packit df99a1
{
Packit df99a1
   int num1=*(int *) ptr1;
Packit df99a1
   int num2=*(int *) ptr2;
Packit df99a1
   return num1<num2 ? -1 : num1>num2 ? 1 : 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
static GList<int>
Packit df99a1
sortList(const GList<int> & list)
Packit df99a1
{
Packit df99a1
   GArray<int> a(list.size()-1);
Packit df99a1
   int cnt;
Packit df99a1
   GPosition pos;
Packit df99a1
   for(pos=list, cnt=0;pos;++pos, cnt++)
Packit df99a1
      a[cnt]=list[pos];
Packit df99a1
Packit df99a1
   qsort((int *) a, a.size(), sizeof(int), cmp);
Packit df99a1
Packit df99a1
   GList<int> l;
Packit df99a1
   for(int i=0;i
Packit df99a1
      l.append(a[i]);
Packit df99a1
Packit df99a1
   return l;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::move_pages(const GList<int> & _page_list, int shift)
Packit df99a1
{
Packit df99a1
   if (!shift) return;
Packit df99a1
Packit df99a1
   GList<int> page_list=sortList(_page_list);
Packit df99a1
Packit df99a1
   GList<GUTF8String> id_list;
Packit df99a1
   for(GPosition pos=page_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
Packit df99a1
      if (frec)
Packit df99a1
         id_list.append(frec->get_load_name());
Packit df99a1
   }
Packit df99a1
Packit df99a1
   if (shift<0)
Packit df99a1
   {
Packit df99a1
         // We have to start here from the smallest page number
Packit df99a1
         // We will move it according to the 'shift', and all
Packit df99a1
         // further moves are guaranteed not to affect its page number.
Packit df99a1
Packit df99a1
         // We will be changing the 'min_page' to make sure that
Packit df99a1
         // pages moved beyond the document will still be in correct order
Packit df99a1
      int min_page=0;
Packit df99a1
      for(GPosition pos=id_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
         GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
Packit df99a1
         if (frec)
Packit df99a1
         {
Packit df99a1
            int page_num=frec->get_page_num();
Packit df99a1
            int new_page_num=page_num+shift;
Packit df99a1
            if (new_page_num
Packit df99a1
               new_page_num=min_page++;
Packit df99a1
            move_page(page_num, new_page_num);
Packit df99a1
         }
Packit df99a1
      }
Packit df99a1
   } else
Packit df99a1
   {
Packit df99a1
         // We have to start here from the biggest page number
Packit df99a1
         // We will move it according to the 'shift', and all
Packit df99a1
         // further moves will not affect its page number.
Packit df99a1
Packit df99a1
         // We will be changing the 'max_page' to make sure that
Packit df99a1
         // pages moved beyond the document will still be in correct order
Packit df99a1
      int max_page=djvm_dir->get_pages_num()-1;
Packit df99a1
      for(GPosition pos=id_list.lastpos();pos;--pos)
Packit df99a1
      {
Packit df99a1
         GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
Packit df99a1
         if (frec)
Packit df99a1
         {
Packit df99a1
            int page_num=frec->get_page_num();
Packit df99a1
            int new_page_num=page_num+shift;
Packit df99a1
            if (new_page_num>max_page)
Packit df99a1
               new_page_num=max_page--;
Packit df99a1
            move_page(page_num, new_page_num);
Packit df99a1
         }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::set_file_name(const GUTF8String &id, const GUTF8String &name)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::set_file_name(), id='" << id << "', name='" << name << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // It's important to get the URL now, because later (after we
Packit df99a1
      // change DjVmDir) id_to_url() will be returning a modified value
Packit df99a1
   GURL url=id_to_url(id);
Packit df99a1
Packit df99a1
      // Change DjVmDir. It will check if the name is unique
Packit df99a1
   djvm_dir->set_file_name(id, name);
Packit df99a1
Packit df99a1
      // Now find DjVuFile (if any) and rename it
Packit df99a1
   GPosition pos;
Packit df99a1
   if (files_map.contains(id, pos))
Packit df99a1
   {
Packit df99a1
      GP<File> file=files_map[pos];
Packit df99a1
      GP<DataPool> pool=file->pool;
Packit df99a1
      if (pool) pool->load_file();
Packit df99a1
      GP<DjVuFile> djvu_file=file->file;
Packit df99a1
      if (djvu_file) djvu_file->set_name(name);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::set_page_name(int page_num, const GUTF8String &name)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::set_page_name(), page_num='" << page_num << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (page_num<0 || page_num>=get_pages_num())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
Packit df99a1
Packit df99a1
   set_file_name(page_to_id(page_num), name);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::set_file_title(const GUTF8String &id, const GUTF8String &title)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::set_file_title(), id='" << id << "', title='" << title << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // Just change DjVmDir. It will check if the title is unique
Packit df99a1
   djvm_dir->set_file_title(id, title);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::set_page_title(int page_num, const GUTF8String &title)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::set_page_title(), page_num='" << page_num << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (page_num<0 || page_num>=get_pages_num())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
Packit df99a1
Packit df99a1
   set_file_title(page_to_id(page_num), title);
Packit df99a1
}
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//************************** Shared annotations ******************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::simplify_anno(void (* progress_cb)(float progress, void *),
Packit df99a1
                             void * cl_data)
Packit df99a1
      // It's important that no decoding is done while this function
Packit df99a1
      // is running. Otherwise the DjVuFile's decoding routines and
Packit df99a1
      // this function may attempt to decode/modify a file's
Packit df99a1
      // annotations at the same time.
Packit df99a1
{
Packit df99a1
      // Get the name of the SHARED_ANNO file. We will not
Packit df99a1
      // touch that file (will not move annotations from it)
Packit df99a1
   GP<DjVmDir::File> shared_file=djvm_dir->get_shared_anno_file();
Packit df99a1
   GUTF8String shared_id;
Packit df99a1
   if (shared_file)
Packit df99a1
      shared_id=shared_file->get_load_name();
Packit df99a1
Packit df99a1
   GList<GURL> ignore_list;
Packit df99a1
   if (shared_id.length())
Packit df99a1
      ignore_list.append(id_to_url(shared_id));
Packit df99a1
Packit df99a1
      // First, for every page get merged (or "flatten" or "projected")
Packit df99a1
      // annotations and store them inside the top-level page file
Packit df99a1
   int pages_num=djvm_dir->get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
   {
Packit df99a1
      GP<DjVuFile> djvu_file=get_djvu_file(page_num);
Packit df99a1
      if (!djvu_file)
Packit df99a1
        G_THROW( ERR_MSG("DjVuDocEditor.page_fail") "\t" + GUTF8String(page_num));
Packit df99a1
      int max_level=0;
Packit df99a1
      GP<ByteStream> anno;
Packit df99a1
      anno=djvu_file->get_merged_anno(ignore_list, &max_level);
Packit df99a1
      if (anno && max_level>0)
Packit df99a1
      {
Packit df99a1
            // This is the moment when we try to modify DjVuFile's annotations
Packit df99a1
            // Make sure, that it's not being decoded
Packit df99a1
         GSafeFlags & file_flags=djvu_file->get_safe_flags();
Packit df99a1
         GMonitorLock lock(&file_flags);
Packit df99a1
         while(file_flags & DjVuFile::DECODING)
Packit df99a1
            file_flags.wait();
Packit df99a1
        
Packit df99a1
            // Merge all chunks in one by decoding and encoding DjVuAnno
Packit df99a1
         const GP<DjVuAnno> dec_anno(DjVuAnno::create());
Packit df99a1
         dec_anno->decode(anno);
Packit df99a1
         const GP<ByteStream> new_anno(ByteStream::create());
Packit df99a1
         dec_anno->encode(new_anno);
Packit df99a1
         new_anno->seek(0);
Packit df99a1
Packit df99a1
            // And store it in the file
Packit df99a1
         djvu_file->anno=new_anno;
Packit df99a1
         djvu_file->rebuild_data_pool();
Packit df99a1
         if ((file_flags & (DjVuFile::DECODE_OK |
Packit df99a1
                            DjVuFile::DECODE_FAILED |
Packit df99a1
                            DjVuFile::DECODE_STOPPED))==0)
Packit df99a1
            djvu_file->anno=0;
Packit df99a1
      }
Packit df99a1
      if (progress_cb)
Packit df99a1
    progress_cb((float)(page_num/2.0/pages_num), cl_data);
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Now remove annotations from every file except for
Packit df99a1
      // the top-level page files and SHARED_ANNO file.
Packit df99a1
      // Unlink empty files too.
Packit df99a1
   GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
Packit df99a1
   int cnt;
Packit df99a1
   GPosition pos;
Packit df99a1
   for(pos=files_list, cnt=0;pos;++pos, cnt++)
Packit df99a1
   {
Packit df99a1
      GP<DjVmDir::File> frec=files_list[pos];
Packit df99a1
      if (!frec->is_page() && frec->get_load_name()!=shared_id)
Packit df99a1
      {
Packit df99a1
         GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name());
Packit df99a1
         if (djvu_file)
Packit df99a1
         {
Packit df99a1
            djvu_file->remove_anno();
Packit df99a1
            if (djvu_file->get_chunks_number()==0)
Packit df99a1
               remove_file(frec->get_load_name(), true);
Packit df99a1
         }
Packit df99a1
      }
Packit df99a1
      if (progress_cb)
Packit df99a1
         progress_cb((float)(0.5+cnt/2.0/files_list.size()), cl_data);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::create_shared_anno_file(void (* progress_cb)(float progress, void *),
Packit df99a1
                                       void * cl_data)
Packit df99a1
{
Packit df99a1
   if (djvm_dir->get_shared_anno_file())
Packit df99a1
      G_THROW( ERR_MSG("DjVuDocEditor.share_fail") );
Packit df99a1
Packit df99a1
      // Prepare file with ANTa chunk inside
Packit df99a1
   const GP<ByteStream> gstr(ByteStream::create());
Packit df99a1
   const GP<IFFByteStream> giff(IFFByteStream::create(gstr));
Packit df99a1
   IFFByteStream &iff=*giff;
Packit df99a1
   iff.put_chunk("FORM:DJVI");
Packit df99a1
   iff.put_chunk("ANTa");
Packit df99a1
   iff.close_chunk();
Packit df99a1
   iff.close_chunk();
Packit df99a1
   ByteStream &str=*gstr;
Packit df99a1
   str.flush();
Packit df99a1
   str.seek(0);
Packit df99a1
   const GP<DataPool> file_pool(DataPool::create(gstr));
Packit df99a1
Packit df99a1
      // Get a unique ID for the new file
Packit df99a1
   const GUTF8String id(find_unique_id("shared_anno.iff"));
Packit df99a1
Packit df99a1
      // Add it into the directory
Packit df99a1
   GP<DjVmDir::File> frec(DjVmDir::File::create(id, id, id,
Packit df99a1
     DjVmDir::File::SHARED_ANNO));
Packit df99a1
   djvm_dir->insert_file(frec, 1);
Packit df99a1
Packit df99a1
      // Add it to our "cache"
Packit df99a1
   {
Packit df99a1
      GP<File> f=new File;
Packit df99a1
      f->pool=file_pool;
Packit df99a1
      GCriticalSectionLock lock(&files_lock);
Packit df99a1
      files_map[id]=f;
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // Now include this shared file into every top-level page file
Packit df99a1
   int pages_num=djvm_dir->get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
   {
Packit df99a1
      GP<DjVuFile> djvu_file=get_djvu_file(page_num);
Packit df99a1
      djvu_file->insert_file(id, 1);
Packit df99a1
Packit df99a1
      if (progress_cb)
Packit df99a1
         progress_cb((float) page_num/pages_num, cl_data);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
DjVuDocEditor::set_djvm_nav(GP<DjVmNav> n)
Packit df99a1
{
Packit df99a1
  if (n && ! n->isValidBookmark())
Packit df99a1
    G_THROW("Invalid bookmark data");
Packit df99a1
  djvm_nav = n;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuFile>
Packit df99a1
DjVuDocEditor::get_shared_anno_file(void)
Packit df99a1
{
Packit df99a1
   GP<DjVuFile> djvu_file;
Packit df99a1
Packit df99a1
   GP<DjVmDir::File> frec=djvm_dir->get_shared_anno_file();
Packit df99a1
   if (frec)
Packit df99a1
      djvu_file=get_djvu_file(frec->get_load_name());
Packit df99a1
Packit df99a1
   return djvu_file;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DjVuDocEditor::get_thumbnail(int page_num, bool dont_decode)
Packit df99a1
      // We override DjVuDocument::get_thumbnail() here because
Packit df99a1
      // pages may have been shuffled and those "thumbnail file records"
Packit df99a1
      // from the DjVmDir do not describe things correctly.
Packit df99a1
      //
Packit df99a1
      // So, first we will check the thumb_map[] if we have a predecoded
Packit df99a1
      // thumbnail for the given page. If this is the case, we will
Packit df99a1
      // return it. Otherwise we will ask DjVuDocument to generate
Packit df99a1
      // this thumbnail for us.
Packit df99a1
{
Packit df99a1
   const GUTF8String id(page_to_id(page_num));
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
   const GPosition pos(thumb_map.contains(id));
Packit df99a1
   if (pos)
Packit df99a1
   {
Packit df99a1
         // Get the image from the map
Packit df99a1
      return thumb_map[pos];
Packit df99a1
   } else
Packit df99a1
   {
Packit df99a1
      unfile_thumbnails();
Packit df99a1
      return DjVuDocument::get_thumbnail(page_num, dont_decode);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocEditor::get_thumbnails_num(void) const
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lock((GCriticalSection *) &thumb_lock);
Packit df99a1
Packit df99a1
   int cnt=0;
Packit df99a1
   int pages_num=get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
   {
Packit df99a1
     if (thumb_map.contains(page_to_id(page_num)))
Packit df99a1
       cnt++;
Packit df99a1
   }
Packit df99a1
   return cnt;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocEditor::get_thumbnails_size(void) const
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock((GCriticalSection *) &thumb_lock);
Packit df99a1
Packit df99a1
   int pages_num=get_pages_num();
Packit df99a1
   for(int page_num=0;page_num
Packit df99a1
   {
Packit df99a1
     const GPosition pos(thumb_map.contains(page_to_id(page_num)));
Packit df99a1
     if (pos)
Packit df99a1
     {
Packit df99a1
       const GP<ByteStream> gstr(thumb_map[pos]->get_stream());
Packit df99a1
       GP<IW44Image> iwpix=IW44Image::create_decode(IW44Image::COLOR);
Packit df99a1
       iwpix->decode_chunk(gstr);
Packit df99a1
      
Packit df99a1
       int width=iwpix->get_width();
Packit df99a1
       int height=iwpix->get_height();
Packit df99a1
       return width
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  return -1;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::remove_thumbnails(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   unfile_thumbnails();
Packit df99a1
Packit df99a1
   DEBUG_MSG("clearing thumb_map\n");
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
   thumb_map.empty();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::unfile_thumbnails(void)
Packit df99a1
      // Will erase all "THUMBNAILS" files from DjVmDir.
Packit df99a1
      // This function is useful when filing thumbnails (to get rid of
Packit df99a1
      // those files, which currently exist: they need to be replaced
Packit df99a1
      // anyway) and when calling DjVuDocument::get_thumbnail() to
Packit df99a1
      // be sure, that it will not use wrong information from DjVmDir
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::unfile_thumbnails(): updating DjVmDir\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   {
Packit df99a1
     GCriticalSectionLock lock(&threqs_lock);
Packit df99a1
     threqs_list.empty();
Packit df99a1
   }
Packit df99a1
   if((const DjVmDir *)djvm_dir)
Packit df99a1
   {
Packit df99a1
     GPList<DjVmDir::File> xfiles_list=djvm_dir->get_files_list();
Packit df99a1
     for(GPosition pos=xfiles_list;pos;++pos)
Packit df99a1
     {
Packit df99a1
       GP<DjVmDir::File> f=xfiles_list[pos];
Packit df99a1
       if (f->is_thumbnails())
Packit df99a1
         djvm_dir->delete_file(f->get_load_name());
Packit df99a1
     }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::file_thumbnails(void)
Packit df99a1
      // The purpose of this function is to create files containing
Packit df99a1
      // thumbnail images and register them in DjVmDir.
Packit df99a1
      // If some of the thumbnail images are missing, they'll
Packit df99a1
      // be generated with generate_thumbnails()
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::file_thumbnails(): updating DjVmDir\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   unfile_thumbnails();
Packit df99a1
Packit df99a1
      // Generate thumbnails if they're missing due to some reason.
Packit df99a1
   int thumb_num=get_thumbnails_num();
Packit df99a1
   int size=thumb_num>0 ? get_thumbnails_size() : 128;
Packit df99a1
   if (thumb_num!=get_pages_num())
Packit df99a1
   {
Packit df99a1
     generate_thumbnails(size);
Packit df99a1
   }
Packit df99a1
Packit df99a1
   DEBUG_MSG("filing thumbnails\n");
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock(&thumb_lock);
Packit df99a1
Packit df99a1
      // The first thumbnail file always contains only one thumbnail
Packit df99a1
   int ipf=1;
Packit df99a1
   int image_num=0;
Packit df99a1
   int page_num=0, pages_num=djvm_dir->get_pages_num();
Packit df99a1
   GP<ByteStream> str(ByteStream::create());
Packit df99a1
   GP<IFFByteStream> iff(IFFByteStream::create(str));
Packit df99a1
   iff->put_chunk("FORM:THUM");
Packit df99a1
   for(;;)
Packit df99a1
   {
Packit df99a1
      GUTF8String id(page_to_id(page_num));
Packit df99a1
      const GPosition pos(thumb_map.contains(id));
Packit df99a1
      if (! pos)
Packit df99a1
      {
Packit df99a1
        G_THROW( ERR_MSG("DjVuDocEditor.no_thumb") "\t"+GUTF8String(page_num));
Packit df99a1
      }
Packit df99a1
      iff->put_chunk("TH44");
Packit df99a1
      iff->copy(*(thumb_map[pos]->get_stream()));
Packit df99a1
      iff->close_chunk();
Packit df99a1
      image_num++;
Packit df99a1
      page_num++;
Packit df99a1
      if (image_num>=ipf || page_num>=pages_num)
Packit df99a1
      {
Packit df99a1
         int i=id.rsearch('.');
Packit df99a1
         if(i<=0)
Packit df99a1
         {
Packit df99a1
           i=id.length();
Packit df99a1
         }
Packit df99a1
         id=id.substr(0,i)+".thumb";
Packit df99a1
            // Get unique ID for this file
Packit df99a1
         id=find_unique_id(id);
Packit df99a1
Packit df99a1
            // Create a file record with the chosen ID
Packit df99a1
         GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id,
Packit df99a1
           DjVmDir::File::THUMBNAILS));
Packit df99a1
Packit df99a1
            // Set correct file position (so that it will cover the next
Packit df99a1
            // ipf pages)
Packit df99a1
         int file_pos=djvm_dir->get_page_pos(page_num-image_num);
Packit df99a1
         djvm_dir->insert_file(file, file_pos);
Packit df99a1
Packit df99a1
            // Now add the File record (containing the file URL and DataPool)
Packit df99a1
            // After we do it a simple save_as() will save the document
Packit df99a1
            // with the thumbnails. This is because DjVuDocument will see
Packit df99a1
            // the file in DjVmDir and will ask for data. We will intercept
Packit df99a1
            // the request for data and will provide this DataPool
Packit df99a1
         iff->close_chunk();
Packit df99a1
         str->seek(0);
Packit df99a1
         const GP<DataPool> file_pool(DataPool::create(str));
Packit df99a1
         GP<File> f=new File;
Packit df99a1
         f->pool=file_pool;
Packit df99a1
         GCriticalSectionLock lock(&files_lock);
Packit df99a1
         files_map[id]=f;
Packit df99a1
Packit df99a1
            // And create new streams
Packit df99a1
         str=ByteStream::create();
Packit df99a1
         iff=IFFByteStream::create(str);
Packit df99a1
         iff->put_chunk("FORM:THUM");
Packit df99a1
         image_num=0;
Packit df99a1
Packit df99a1
            // Reset ipf to correct value (after we stored first
Packit df99a1
            // "exceptional" file with thumbnail for the first page)
Packit df99a1
         if (page_num==1) ipf=thumbnails_per_file;
Packit df99a1
         if (page_num>=pages_num) break;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocEditor::generate_thumbnails(int thumb_size, int page_num)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::generate_thumbnails(): doing it\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if(page_num<(djvm_dir->get_pages_num()))
Packit df99a1
   {
Packit df99a1
      const GUTF8String id(page_to_id(page_num));
Packit df99a1
      if (!thumb_map.contains(id))
Packit df99a1
        {
Packit df99a1
          const GP<DjVuImage> dimg(get_page(page_num, true));
Packit df99a1
         
Packit df99a1
          GRect rect(0, 0, thumb_size, dimg->get_height()*thumb_size/dimg->get_width());
Packit df99a1
          GP<GPixmap> pm=dimg->get_pixmap(rect, rect, get_thumbnails_gamma());
Packit df99a1
          if (!pm)
Packit df99a1
            {
Packit df99a1
              const 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(), &GPixel::WHITE);
Packit df99a1
            }
Packit df99a1
          // Store and compress the pixmap
Packit df99a1
          const GP<IW44Image> iwpix(IW44Image::create_encode(*pm));
Packit df99a1
          const 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
          gstr->seek(0L);
Packit df99a1
          thumb_map[id]=DataPool::create(gstr);
Packit df99a1
        }
Packit df99a1
      ++page_num;
Packit df99a1
   }
Packit df99a1
   else
Packit df99a1
   {
Packit df99a1
     page_num = -1;
Packit df99a1
   }
Packit df99a1
   return page_num;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::generate_thumbnails(int thumb_size,
Packit df99a1
                                   bool (* cb)(int page_num, void *),
Packit df99a1
                                   void * cl_data)
Packit df99a1
{
Packit df99a1
   int page_num=0;
Packit df99a1
   do
Packit df99a1
   {
Packit df99a1
     page_num=generate_thumbnails(thumb_size,page_num);
Packit df99a1
     if (cb && page_num>0) if (cb(page_num-1, cl_data)) return;
Packit df99a1
   } while(page_num>=0);
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
store_file(const GP<DjVmDir> & src_djvm_dir, const GP<DjVmDoc> & djvm_doc,
Packit df99a1
           GP<DjVuFile> & djvu_file, GMap<GURL, void *> & map)
Packit df99a1
{
Packit df99a1
   GURL url=djvu_file->get_url();
Packit df99a1
   if (!map.contains(url))
Packit df99a1
   {
Packit df99a1
      map[url]=0;
Packit df99a1
Packit df99a1
         // Store included files first
Packit df99a1
      GPList<DjVuFile> djvu_files_list=djvu_file->get_included_files(false);
Packit df99a1
      for(GPosition pos=djvu_files_list;pos;++pos)
Packit df99a1
         store_file(src_djvm_dir, djvm_doc, djvu_files_list[pos], map);
Packit df99a1
Packit df99a1
         // Now store contents of this file
Packit df99a1
      GP<DataPool> file_data=djvu_file->get_djvu_data(false);
Packit df99a1
      GP<DjVmDir::File> frec=src_djvm_dir->name_to_file(url.name());
Packit df99a1
      if (frec)
Packit df99a1
      {
Packit df99a1
         frec=new DjVmDir::File(*frec);
Packit df99a1
         djvm_doc->insert_file(frec, file_data, -1);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::save_pages_as(
Packit df99a1
  const GP<ByteStream> &str, const GList<int> & _page_list)
Packit df99a1
{
Packit df99a1
   GList<int> page_list=sortList(_page_list);
Packit df99a1
Packit df99a1
   GP<DjVmDoc> djvm_doc=DjVmDoc::create();
Packit df99a1
   GMap<GURL, void *> map;
Packit df99a1
   for(GPosition pos=page_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
Packit df99a1
      if (frec)
Packit df99a1
      {
Packit df99a1
         GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name());
Packit df99a1
         if (djvu_file)
Packit df99a1
            store_file(djvm_dir, djvm_doc, djvu_file, map);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
   djvm_doc->write(str);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::save_file(const GUTF8String &file_id, const GURL &codebase,
Packit df99a1
  const bool only_modified, GMap<GUTF8String,GUTF8String> & map)
Packit df99a1
{
Packit df99a1
  if(only_modified)
Packit df99a1
  {
Packit df99a1
    for(GPosition pos=files_map;pos;++pos)
Packit df99a1
    {
Packit df99a1
      const GP<File> file_rec(files_map[pos]);
Packit df99a1
      const bool file_modified=file_rec->pool ||
Packit df99a1
        (file_rec->file && file_rec->file->is_modified());
Packit df99a1
      if(!file_modified)
Packit df99a1
      {
Packit df99a1
        const GUTF8String id=files_map.key(pos);
Packit df99a1
        const GUTF8String save_name(djvm_dir->id_to_file(id)->get_save_name());
Packit df99a1
        if(id == save_name)
Packit df99a1
        {
Packit df99a1
          map[id]=id;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  save_file(file_id,codebase,map);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::save_file(
Packit df99a1
  const GUTF8String &file_id, const GURL &codebase,
Packit df99a1
  GMap<GUTF8String,GUTF8String> & map)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::save_file(): ID='" << file_id << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (!map.contains(file_id))
Packit df99a1
   {
Packit df99a1
      const GP<DjVmDir::File> file(djvm_dir->id_to_file(file_id));
Packit df99a1
Packit df99a1
      GP<DataPool> file_pool;
Packit df99a1
      const GPosition pos(files_map.contains(file_id));
Packit df99a1
      if (pos)
Packit df99a1
      {
Packit df99a1
         const GP<File> file_rec(files_map[pos]);
Packit df99a1
         if (file_rec->file)
Packit df99a1
            file_pool=file_rec->file->get_djvu_data(false);
Packit df99a1
         else
Packit df99a1
            file_pool=file_rec->pool;
Packit df99a1
      }
Packit df99a1
Packit df99a1
      if (!file_pool)
Packit df99a1
      {
Packit df99a1
         DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
Packit df99a1
         file_pool=pcaster->request_data(this, id_to_url(file_id));
Packit df99a1
      }
Packit df99a1
Packit df99a1
      if (file_pool)
Packit df99a1
      {
Packit df99a1
         GMap<GUTF8String,GUTF8String> incl;
Packit df99a1
         map[file_id]=get_djvm_doc()->save_file(codebase,*file,incl,file_pool);
Packit df99a1
         for(GPosition pos=incl;pos;++pos)
Packit df99a1
         {
Packit df99a1
           save_file(incl.key(pos),codebase ,map);
Packit df99a1
         }
Packit df99a1
      }else
Packit df99a1
      {
Packit df99a1
        map[file_id]=file->get_save_name();
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::save(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::save(): saving the file\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (!can_be_saved())
Packit df99a1
     G_THROW( ERR_MSG("DjVuDocEditor.cant_save") );
Packit df99a1
   save_as(GURL(), orig_doc_type!=INDIRECT);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::write(const GP<ByteStream> &gbs, bool force_djvm)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocEditor::write()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  if (get_thumbnails_num()==get_pages_num())
Packit df99a1
  {
Packit df99a1
    file_thumbnails();
Packit df99a1
  }else
Packit df99a1
  { 
Packit df99a1
    remove_thumbnails();
Packit df99a1
  }
Packit df99a1
  clean_files_map();
Packit df99a1
  DjVuDocument::write(gbs,force_djvm);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::write(
Packit df99a1
  const GP<ByteStream> &gbs,const GMap<GUTF8String,void *> &reserved)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DjVuDocEditor::write()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  if (get_thumbnails_num()==get_pages_num())
Packit df99a1
  {
Packit df99a1
    file_thumbnails();
Packit df99a1
  }else
Packit df99a1
  { 
Packit df99a1
    remove_thumbnails();
Packit df99a1
  }
Packit df99a1
  clean_files_map();
Packit df99a1
  DjVuDocument::write(gbs,reserved);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DjVuDocEditor::save_as(const GURL &where, bool bundled)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DjVuDocEditor::save_as(): where='" << where << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
      // First see if we need to generate (or just reshuffle) thumbnails...
Packit df99a1
      // If we have an icon for every page, we will just call
Packit df99a1
      // file_thumbnails(), which will update DjVmDir and will create
Packit df99a1
      // the actual bundles with thumbnails (very fast)
Packit df99a1
      // Otherwise we will remove the thumbnails completely because
Packit df99a1
      // we really don't want to deal with documents, which have only
Packit df99a1
      // some of their pages thumbnailed.
Packit df99a1
   if (get_thumbnails_num()==get_pages_num())
Packit df99a1
   {
Packit df99a1
     file_thumbnails();
Packit df99a1
   }else
Packit df99a1
   { 
Packit df99a1
     remove_thumbnails();
Packit df99a1
   }
Packit df99a1
Packit df99a1
   GURL save_doc_url;
Packit df99a1
Packit df99a1
   if (where.is_empty())
Packit df99a1
   {
Packit df99a1
         // Assume, that we just want to 'save'. Check, that it's possible
Packit df99a1
         // and proceed.
Packit df99a1
      bool can_be_saved_bundled =
Packit df99a1
	orig_doc_type==BUNDLED ||
Packit df99a1
	orig_doc_type==OLD_BUNDLED ||
Packit df99a1
	orig_doc_type==SINGLE_PAGE ||
Packit df99a1
	(orig_doc_type==OLD_INDEXED && orig_doc_pages==1);
Packit df99a1
      if ((bundled ^ can_be_saved_bundled)!=0)
Packit df99a1
         G_THROW( ERR_MSG("DjVuDocEditor.cant_save2") );
Packit df99a1
      save_doc_url=doc_url;
Packit df99a1
   } else
Packit df99a1
   {
Packit df99a1
      save_doc_url=where;
Packit df99a1
   }
Packit df99a1
Packit df99a1
   int save_doc_type=bundled ? BUNDLED : INDIRECT;
Packit df99a1
Packit df99a1
   clean_files_map();
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock(&files_lock);
Packit df99a1
Packit df99a1
   DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
Packit df99a1
Packit df99a1
      // First consider saving in SINGLE_FILE format (one file)
Packit df99a1
   if(needs_compression())
Packit df99a1
   {
Packit df99a1
     DEBUG_MSG("Compressing on output\n");
Packit df99a1
     remove_thumbnails();
Packit df99a1
     if(! djvu_compress_codec)
Packit df99a1
     {
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocEditor.no_codec") );
Packit df99a1
     }
Packit df99a1
     const GP<DjVmDoc> doc(get_djvm_doc());
Packit df99a1
     GP<ByteStream> mbs(ByteStream::create());
Packit df99a1
     doc->write(mbs);
Packit df99a1
     mbs->flush();
Packit df99a1
     mbs->seek(0,SEEK_SET);
Packit df99a1
     djvu_compress_codec(mbs,save_doc_url,(!(const DjVmDir *)djvm_dir)||(djvm_dir->get_files_num()==1)||(save_doc_type!=INDIRECT));
Packit df99a1
     files_map.empty();
Packit df99a1
     doc_url=GURL();
Packit df99a1
   }else
Packit df99a1
   {
Packit df99a1
     bool singlepage = (djvm_dir->get_files_num()==1 && !djvm_nav);
Packit df99a1
     if (singlepage)
Packit df99a1
     {
Packit df99a1
       // maybe save as single page
Packit df99a1
       DjVmDir::File *file = djvm_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
       // Here 'bundled' has no effect: we will save it as one page.
Packit df99a1
       DEBUG_MSG("saving one file...\n");
Packit df99a1
       GURL file_url=page_to_url(0);
Packit df99a1
       const GUTF8String file_id(djvm_dir->page_to_file(0)->get_load_name());
Packit df99a1
       GP<DataPool> file_pool;
Packit df99a1
       GPosition pos=files_map.contains(file_id);
Packit df99a1
       if (pos)
Packit df99a1
       {
Packit df99a1
         const GP<File> file_rec(files_map[pos]);
Packit df99a1
         if (file_rec->pool && (!file_rec->file ||
Packit df99a1
                                !file_rec->file->is_modified()))
Packit df99a1
         {
Packit df99a1
           file_pool=file_rec->pool;
Packit df99a1
         }else if (file_rec->file)
Packit df99a1
         {
Packit df99a1
           file_pool=file_rec->file->get_djvu_data(false);
Packit df99a1
         }
Packit df99a1
       }
Packit df99a1
       // Even if file has not been modified (pool==0) we still want
Packit df99a1
       // to save it.
Packit df99a1
       if (!file_pool)
Packit df99a1
         file_pool=pcaster->request_data(this, file_url);
Packit df99a1
       if (file_pool)
Packit df99a1
       {
Packit df99a1
         DEBUG_MSG("Saving '" << file_url << "' to '" << save_doc_url << "'\n");
Packit df99a1
         DataPool::load_file(save_doc_url);
Packit df99a1
         const GP<ByteStream> gstr_out(ByteStream::create(save_doc_url, "wb"));
Packit df99a1
         ByteStream &str_out=*gstr_out;
Packit df99a1
         str_out.writall(octets, 4);
Packit df99a1
         const GP<ByteStream> str_in(file_pool->get_stream());
Packit df99a1
         str_out.copy(*str_in);
Packit df99a1
       }
Packit df99a1
Packit df99a1
       // Update the document's DataPool (to save memory)
Packit df99a1
       const GP<DjVmDoc> doc(get_djvm_doc());
Packit df99a1
       const GP<ByteStream> gstr=ByteStream::create();// One page: we can do it in the memory
Packit df99a1
       doc->write(gstr);
Packit df99a1
       gstr->seek(0, SEEK_SET);
Packit df99a1
       const GP<DataPool> pool(DataPool::create(gstr));
Packit df99a1
       doc_pool=pool;
Packit df99a1
       init_data_pool=pool;
Packit df99a1
Packit df99a1
         // Also update DjVmDir (to reflect changes in offsets)
Packit df99a1
       djvm_dir=doc->get_djvm_dir();
Packit df99a1
     } else if (save_doc_type==INDIRECT)
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("Saving in INDIRECT format to '" << save_doc_url << "'\n");
Packit df99a1
       bool save_only_modified=!(save_doc_url!=doc_url || save_doc_type!=orig_doc_type);
Packit df99a1
       GPList<DjVmDir::File> xfiles_list=djvm_dir->resolve_duplicates(false);
Packit df99a1
       const GURL codebase=save_doc_url.base();
Packit df99a1
       int pages_num=djvm_dir->get_pages_num();
Packit df99a1
       GMap<GUTF8String, GUTF8String> map;
Packit df99a1
       // First go thru the pages
Packit df99a1
       for(int page_num=0;page_num
Packit df99a1
       {
Packit df99a1
         const GUTF8String id(djvm_dir->page_to_file(page_num)->get_load_name());
Packit df99a1
         save_file(id, codebase, save_only_modified, map);
Packit df99a1
       }
Packit df99a1
       // Next go thru thumbnails and similar stuff
Packit df99a1
       GPosition pos;
Packit df99a1
       for(pos=xfiles_list;pos;++pos)
Packit df99a1
         save_file(xfiles_list[pos]->get_load_name(), codebase, save_only_modified, map);
Packit df99a1
Packit df99a1
         // Finally - save the top-level index file
Packit df99a1
       for(pos=xfiles_list;pos;++pos)
Packit df99a1
       {
Packit df99a1
         const GP<DjVmDir::File> file(xfiles_list[pos]);
Packit df99a1
         file->offset=0;
Packit df99a1
         file->size=0;
Packit df99a1
       }
Packit df99a1
       DataPool::load_file(save_doc_url);
Packit df99a1
       const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb"));
Packit df99a1
       const GP<IFFByteStream> giff(IFFByteStream::create(gstr));
Packit df99a1
       IFFByteStream &iff=*giff;
Packit df99a1
Packit df99a1
       iff.put_chunk("FORM:DJVM", 1);
Packit df99a1
       iff.put_chunk("DIRM");
Packit df99a1
       djvm_dir->encode(giff->get_bytestream());
Packit df99a1
       iff.close_chunk();
Packit df99a1
       if (djvm_nav)
Packit df99a1
         {
Packit df99a1
           iff.put_chunk("NAVM");
Packit df99a1
           djvm_nav->encode(iff.get_bytestream());
Packit df99a1
           iff.close_chunk();
Packit df99a1
         }
Packit df99a1
       iff.close_chunk();
Packit df99a1
       iff.flush();
Packit df99a1
Packit df99a1
       // Update the document data pool (not required, but will save memory)
Packit df99a1
       doc_pool=DataPool::create(save_doc_url);
Packit df99a1
       init_data_pool=doc_pool;
Packit df99a1
Packit df99a1
       // No reason to update DjVmDir as for this format it doesn't
Packit df99a1
       // contain DJVM offsets
Packit df99a1
     } else if (save_doc_type==BUNDLED || save_doc_type==OLD_BUNDLED)
Packit df99a1
     {
Packit df99a1
        DEBUG_MSG("Saving in BUNDLED format to '" << save_doc_url << "'\n");
Packit df99a1
Packit df99a1
         // Can't be very smart here. Simply overwrite the file.
Packit df99a1
        const GP<DjVmDoc> doc(get_djvm_doc());
Packit df99a1
        DataPool::load_file(save_doc_url);
Packit df99a1
        const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb"));
Packit df99a1
        doc->write(gstr);
Packit df99a1
        gstr->flush();
Packit df99a1
Packit df99a1
         // Update the document data pool (not required, but will save memory)
Packit df99a1
        doc_pool=DataPool::create(save_doc_url);
Packit df99a1
        init_data_pool=doc_pool;
Packit df99a1
Packit df99a1
         // Also update DjVmDir (to reflect changes in offsets)
Packit df99a1
        djvm_dir=doc->get_djvm_dir();
Packit df99a1
     } else
Packit df99a1
     {
Packit df99a1
       G_THROW( ERR_MSG("DjVuDocEditor.cant_save") );
Packit df99a1
     }
Packit df99a1
Packit df99a1
        // Now, after we have saved the document w/o any error, detach DataPools,
Packit df99a1
        // which are in the 'File's list to save memory. Detach everything.
Packit df99a1
        // Even in the case when File->file is non-zero. If File->file is zero,
Packit df99a1
        // remove the item from the list at all. If it's non-zero, it has
Packit df99a1
        // to stay there because by definition files_map[] contains the list
Packit df99a1
        // of all active files and customized DataPools
Packit df99a1
        //
Packit df99a1
        // In addition to it, look thru all active files and change their URLs
Packit df99a1
        // to reflect changes in the document's URL (if there was a change)
Packit df99a1
        // Another reason why file's URLs must be changed is that we may have
Packit df99a1
        // saved the document in a different format, which changes the rules
Packit df99a1
        // of file url composition.
Packit df99a1
     for(GPosition pos=files_map;pos;)
Packit df99a1
       {
Packit df99a1
	 const GP<File> file_rec(files_map[pos]);
Packit df99a1
	 file_rec->pool=0;
Packit df99a1
	 if (file_rec->file==0)
Packit df99a1
	   {
Packit df99a1
	     GPosition this_pos=pos;
Packit df99a1
	     ++pos;
Packit df99a1
	     files_map.del(this_pos);
Packit df99a1
	   } else
Packit df99a1
	   {
Packit df99a1
	     // Change the file's url;
Packit df99a1
	     if (doc_url!=save_doc_url ||
Packit df99a1
		 orig_doc_type!=save_doc_type)
Packit df99a1
	       {
Packit df99a1
		 if (save_doc_type==BUNDLED)
Packit df99a1
		   file_rec->file->move(save_doc_url);
Packit df99a1
		 else
Packit df99a1
		   file_rec->file->move(save_doc_url.base());
Packit df99a1
	       }
Packit df99a1
	     ++pos;
Packit df99a1
	   }
Packit df99a1
       }
Packit df99a1
   }
Packit df99a1
   orig_doc_type=save_doc_type;
Packit df99a1
   doc_type=save_doc_type;
Packit df99a1
Packit df99a1
   if (doc_url!=save_doc_url)
Packit df99a1
   {
Packit df99a1
     // Also update document's URL (we moved, didn't we?)
Packit df99a1
     doc_url=save_doc_url;
Packit df99a1
     init_url=save_doc_url;
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocEditor> 
Packit df99a1
DjVuDocEditor::create_wait(void)
Packit df99a1
{
Packit df99a1
  DjVuDocEditor *doc=new DjVuDocEditor();
Packit df99a1
  const GP<DjVuDocEditor> retval(doc);
Packit df99a1
  doc->init();
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuDocEditor> 
Packit df99a1
DjVuDocEditor::create_wait(const GURL &url)
Packit df99a1
{
Packit df99a1
  DjVuDocEditor *doc=new DjVuDocEditor();
Packit df99a1
  const GP<DjVuDocEditor> retval(doc);
Packit df99a1
  doc->init(url);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuDocEditor::inherits(const GUTF8String &class_name) const
Packit df99a1
{
Packit df99a1
   return (class_name == "DjVuDocEditor")||DjVuDocument::inherits(class_name);
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocEditor::get_orig_doc_type(void) const
Packit df99a1
{
Packit df99a1
   return orig_doc_type;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DjVuDocEditor::can_be_saved(void) const
Packit df99a1
{
Packit df99a1
   return !(needs_rename()||needs_compression()||orig_doc_type==UNKNOWN_TYPE ||
Packit df99a1
	    orig_doc_type==OLD_INDEXED);
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DjVuDocEditor::get_save_doc_type(void) const
Packit df99a1
{
Packit df99a1
   if (orig_doc_type==SINGLE_PAGE)
Packit df99a1
      if (djvm_dir->get_files_num()==1)
Packit df99a1
        return SINGLE_PAGE;
Packit df99a1
      else
Packit df99a1
        return BUNDLED;
Packit df99a1
   else if (orig_doc_type==INDIRECT)
Packit df99a1
     return INDIRECT;
Packit df99a1
   else if (orig_doc_type==OLD_BUNDLED || orig_doc_type==BUNDLED)
Packit df99a1
     return BUNDLED;
Packit df99a1
   else
Packit df99a1
     return UNKNOWN_TYPE;
Packit df99a1
}
Packit df99a1
Packit df99a1
GURL
Packit df99a1
DjVuDocEditor::get_doc_url(void) const
Packit df99a1
{
Packit df99a1
   return doc_url.is_empty() ? init_url : doc_url;
Packit df99a1
}
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