00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063 
00064 #include "DjVuDocument.h"
00065 #include "DjVmDoc.h"
00066 #include "DjVmDir0.h"
00067 #include "DjVmNav.h"
00068 #include "DjVuNavDir.h"
00069 #include "DjVuImage.h"
00070 #include "DjVuFileCache.h"
00071 #include "IFFByteStream.h"
00072 #include "GOS.h"
00073 #include "DataPool.h"
00074 #include "IW44Image.h"
00075 #include "GRect.h"
00076 
00077 #include "debug.h"
00078 
00079 
00080 #ifdef HAVE_NAMESPACES
00081 namespace DJVU {
00082 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00083 }
00084 #endif
00085 #endif
00086 
00087 
00088 static const char octets[4]={0x41,0x54,0x26,0x54};
00089 const float DjVuDocument::thumb_gamma=(float)2.20;
00090 
00091 void (* DjVuDocument::djvu_import_codec)(
00092   GP<DataPool> &pool, const GURL &url, bool &needs_compression,
00093   bool &needs_rename )=0;
00094 
00095 void (* DjVuDocument::djvu_compress_codec)(
00096   GP<ByteStream> &doc,const GURL &where,bool bundled)=0;
00097 
00098 void
00099 DjVuDocument::set_import_codec(
00100   void (*codec)(
00101     GP<DataPool> &pool, const GURL &url, bool &needs_compression, bool &needs_rename ))
00102 {
00103   djvu_import_codec=codec;
00104 }
00105 
00106 void
00107 DjVuDocument::set_compress_codec(
00108   void (* codec)(
00109     GP<ByteStream> &doc,const GURL &where,bool bundled))
00110 {
00111   djvu_compress_codec=codec;
00112 }
00113 
00114 DjVuDocument::DjVuDocument(void)
00115   : doc_type(UNKNOWN_TYPE),
00116     needs_compression_flag(false),
00117     can_compress_flag(false),
00118     needs_rename_flag(false),
00119     has_url_names(false),
00120     recover_errors(ABORT),
00121     verbose_eof(false),
00122     init_started(false),
00123     cache(0) 
00124 {
00125 }
00126 
00127 GP<DjVuDocument>
00128 DjVuDocument::create(
00129   GP<DataPool> pool, GP<DjVuPort> xport, DjVuFileCache * const xcache)
00130 {
00131   DjVuDocument *doc=new DjVuDocument;
00132   GP<DjVuDocument> retval=doc;
00133   doc->init_data_pool=pool;
00134   doc->start_init(GURL(),xport,xcache);
00135   return retval;
00136 }
00137 
00138 GP<DjVuDocument>
00139 DjVuDocument::create(
00140   const GP<ByteStream> &bs, GP<DjVuPort> xport, DjVuFileCache * const xcache)
00141 {
00142   return create(DataPool::create(bs),xport,xcache);
00143 }
00144 
00145 GP<DjVuDocument>
00146 DjVuDocument::create_wait(
00147   const GURL &url, GP<DjVuPort> xport, DjVuFileCache * const xcache)
00148 {
00149   GP<DjVuDocument> retval=create(url,xport,xcache);
00150   retval->wait_for_complete_init();
00151   return retval;
00152 }
00153 
00154 void
00155 DjVuDocument::start_init(
00156   const GURL & url, GP<DjVuPort> xport, DjVuFileCache * xcache)
00157 {
00158    DEBUG_MSG("DjVuDocument::start_init(): initializing class...\n");
00159    DEBUG_MAKE_INDENT(3);
00160    if (init_started)
00161       G_THROW( ERR_MSG("DjVuDocument.2nd_init") );
00162    if (!get_count())
00163       G_THROW( ERR_MSG("DjVuDocument.not_secure") );
00164    if(url.is_empty())
00165    {
00166      if (!init_data_pool)
00167        G_THROW( ERR_MSG("DjVuDocument.empty_url") );
00168      if(init_url.is_empty())
00169      {
00170        init_url=invent_url("document.djvu");
00171      }
00172    }else
00173    {
00174      init_url=url;
00175    }
00176    
00177       
00178    cache=xcache;
00179    doc_type=UNKNOWN_TYPE;
00180    DjVuPortcaster * pcaster=get_portcaster();
00181    if (!xport)
00182      xport=simple_port=new DjVuSimplePort();
00183    pcaster->add_route(this, xport);
00184    pcaster->add_route(this, this);
00185 
00186    if(!url.is_empty())
00187    {
00188      init_data_pool=pcaster->request_data(this, init_url);
00189      if(init_data_pool)
00190      {
00191        if(!init_url.is_empty() && init_url.is_local_file_url() && djvu_import_codec)
00192        {
00193          djvu_import_codec(init_data_pool,init_url,needs_compression_flag,needs_rename_flag);
00194        }
00195        if(needs_rename_flag)
00196          can_compress_flag=true;
00197      }
00198      if (!init_data_pool) 
00199      {
00200        G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+init_url.get_string());
00201      }
00202    }
00203       
00204    init_started=true;
00205 
00206    init_thread_flags=STARTED;
00207    init_life_saver=this;
00208    init_thr.create(static_init_thread, this);
00209 }
00210 
00211 DjVuDocument::~DjVuDocument(void)
00212 {
00213       
00214    get_portcaster()->del_port(this);
00215 
00216       
00217       
00218       
00219       
00220    {
00221       GCriticalSectionLock lock(&ufiles_lock);
00222       for(GPosition pos=ufiles_list;pos;++pos)
00223       {
00224           GP<DjVuFile> file=ufiles_list[pos]->file;
00225           file->stop_decode(false);
00226           file->stop(false);    
00227       }
00228       ufiles_list.empty();
00229    }
00230 
00231    GPList<DjVuPort> ports=get_portcaster()->prefix_to_ports(get_int_prefix());
00232    for(GPosition pos=ports;pos;++pos)
00233    {
00234      GP<DjVuPort> port=ports[pos];
00235      if (port->inherits("DjVuFile"))
00236      {
00237        DjVuFile * file=(DjVuFile *) (DjVuPort *) port;
00238        file->stop_decode(false);
00239        file->stop(false);   
00240      }
00241    }
00242    DataPool::close_all();
00243 }
00244 
00245 void
00246 DjVuDocument::stop_init(void)
00247 {
00248    DEBUG_MSG("DjVuDocument::stop_init(): making sure that the init thread dies.\n");
00249    DEBUG_MAKE_INDENT(3);
00250 
00251    GMonitorLock lock(&init_thread_flags);
00252    while((init_thread_flags & STARTED) &&
00253      !(init_thread_flags & FINISHED))
00254    {
00255       if (init_data_pool) init_data_pool->stop(true);   
00256 
00257       if (ndir_file) ndir_file->stop(false);
00258 
00259       {
00260      GCriticalSectionLock lock(&ufiles_lock);
00261      for(GPosition pos=ufiles_list;pos;++pos)
00262         ufiles_list[pos]->file->stop(false);    
00263      ufiles_list.empty();
00264       }
00265 
00266       init_thread_flags.wait(50);
00267    }
00268 }
00269 
00270 void
00271 DjVuDocument::check() const
00272 {
00273   if (!init_started)
00274     G_THROW( ERR_MSG("DjVuDocument.not_init") );
00275 }
00276 
00277 void
00278 DjVuDocument::static_init_thread(void * cl_data)
00279 {
00280   DjVuDocument * th=(DjVuDocument *) cl_data;
00281   GP<DjVuDocument> life_saver=th;
00282   th->init_life_saver=0;
00283   G_TRY {
00284     th->init_thread();
00285   } G_CATCH(exc) {
00286     th->flags|=DjVuDocument::DOC_INIT_FAILED;
00287     G_TRY {
00288       th->check_unnamed_files();
00289       if (!exc.cmp_cause(ByteStream::EndOfFile) && th->verbose_eof)
00290         get_portcaster()->notify_error(th, ERR_MSG("DjVuDocument.init_eof") );
00291       else if (!exc.cmp_cause(DataPool::Stop))
00292         get_portcaster()->notify_status(th, ERR_MSG("DjVuDocument.stopped") );
00293       else
00294         get_portcaster()->notify_error(th, exc.get_cause());
00295     } G_CATCH_ALL {} G_ENDCATCH;
00296     th->init_thread_flags|=FINISHED;
00297   } G_ENDCATCH;
00298 }
00299 
00300 void
00301 DjVuDocument::init_thread(void)
00302       
00303       
00304       
00305 {
00306    DEBUG_MSG("DjVuDocument::init_thread(): guessing what we're dealing with\n");
00307    DEBUG_MAKE_INDENT(3);
00308 
00309    DjVuPortcaster * pcaster=get_portcaster();
00310       
00311    GP<ByteStream> stream=init_data_pool->get_stream();
00312 
00313    GP<IFFByteStream> giff=IFFByteStream::create(stream);
00314    IFFByteStream &iff=*giff;
00315    GUTF8String chkid;
00316    int size=iff.get_chunk(chkid);
00317    if (!size)
00318      G_THROW( ByteStream::EndOfFile );
00319    if (size < 0)
00320      G_THROW( ERR_MSG("DjVuDocument.no_file") );
00321    if (size<8)
00322    {
00323      G_THROW( ERR_MSG("DjVuDocument.not_DjVu") );
00324    }
00325    if (chkid=="FORM:DJVM")
00326    {
00327      DEBUG_MSG("Got DJVM document here\n");
00328      DEBUG_MAKE_INDENT(3);
00329      
00330      size=iff.get_chunk(chkid);
00331      if (chkid=="DIRM")
00332        {
00333      djvm_dir=DjVmDir::create();
00334      djvm_dir->decode(iff.get_bytestream());
00335      iff.close_chunk();
00336      if (djvm_dir->is_bundled())
00337            {
00338              DEBUG_MSG("Got BUNDLED file.\n");
00339              doc_type=BUNDLED;
00340            } 
00341          else
00342            {
00343              DEBUG_MSG("Got INDIRECT file.\n");
00344              doc_type=INDIRECT;
00345            }
00346      flags|=DOC_TYPE_KNOWN | DOC_DIR_KNOWN;
00347      pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN | DOC_DIR_KNOWN, 0);
00348      check_unnamed_files();
00349          
00350          
00351          size=iff.get_chunk(chkid);
00352          if (size && chkid=="NAVM")
00353            {
00354              djvm_nav=DjVmNav::create();
00355              djvm_nav->decode(iff.get_bytestream());
00356              iff.close_chunk();
00357            }
00358        }
00359      else if (chkid=="DIR0")
00360        {
00361      DEBUG_MSG("Got OLD_BUNDLED file.\n");
00362      doc_type=OLD_BUNDLED;
00363      flags|=DOC_TYPE_KNOWN;
00364      pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0);
00365      check_unnamed_files();
00366        } 
00367      else 
00368        G_THROW( ERR_MSG("DjVuDocument.bad_format") );
00369      
00370      if (doc_type==OLD_BUNDLED)
00371        {
00372          
00373          
00374          
00375          
00376          
00377      djvm_dir0=DjVmDir0::create();
00378      djvm_dir0->decode(*iff.get_bytestream());
00379      iff.close_chunk();
00380          
00381      int first_page_offset=0;
00382      while(!first_page_offset)
00383            {
00384              int offset;
00385              size=iff.get_chunk(chkid, &offset);
00386              if (size==0) G_THROW( ERR_MSG("DjVuDocument.no_page") );
00387              if (chkid=="FORM:DJVU" || chkid=="FORM:PM44" || chkid=="FORM:BM44")
00388                {
00389                  DEBUG_MSG("Got 1st page offset=" << offset << "\n");
00390                  first_page_offset=offset;
00391                }
00392              iff.close_chunk();
00393            }
00394          
00395          
00396      int file_num;
00397      for(file_num=0;file_num<djvm_dir0->get_files_num();file_num++)
00398            {
00399              DjVmDir0::FileRec & file=*djvm_dir0->get_file(file_num);
00400              if (file.offset==first_page_offset)
00401                {
00402                  first_page_name=file.name;
00403                  break;
00404                }
00405            }
00406      if (!first_page_name.length())
00407            G_THROW( ERR_MSG("DjVuDocument.no_page") );
00408      flags|=DOC_DIR_KNOWN;
00409      pcaster->notify_doc_flags_changed(this, DOC_DIR_KNOWN, 0);
00410      check_unnamed_files();
00411        }
00412    } 
00413    else 
00414      {
00415        
00416        DEBUG_MSG("Got DJVU OLD_INDEXED or SINGLE_PAGE document here.\n");
00417        doc_type=SINGLE_PAGE;
00418        flags|=DOC_TYPE_KNOWN;
00419        pcaster->notify_doc_flags_changed(this, DOC_TYPE_KNOWN, 0);
00420        check_unnamed_files();
00421      }
00422    if (doc_type==OLD_BUNDLED || doc_type==SINGLE_PAGE)
00423      {
00424        DEBUG_MSG("Searching for NDIR chunks...\n");
00425        ndir_file=get_djvu_file(-1);
00426        if (ndir_file) ndir=ndir_file->decode_ndir();
00427        ndir_file=0; 
00428        if (!ndir)
00429          {
00430            
00431            if (doc_type==OLD_BUNDLED)
00432              {
00433                ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url));
00434                ndir->insert_page(-1, first_page_name);
00435              } 
00436            else
00437              {
00438                ndir=DjVuNavDir::create(GURL::UTF8("directory",init_url.base()));
00439                ndir->insert_page(-1, init_url.fname());
00440              }
00441          } 
00442        else
00443          {
00444            if (doc_type==SINGLE_PAGE)
00445              doc_type=OLD_INDEXED;
00446          }
00447        flags|=DOC_NDIR_KNOWN;
00448        pcaster->notify_doc_flags_changed(this, DOC_NDIR_KNOWN, 0);
00449        check_unnamed_files();
00450      }
00451    
00452    flags|=DOC_INIT_OK;
00453    pcaster->notify_doc_flags_changed(this, DOC_INIT_OK, 0);
00454    check_unnamed_files();
00455    init_thread_flags|=FINISHED;
00456    DEBUG_MSG("DOCUMENT IS FULLY INITIALIZED now: doc_type='" <<
00457          (doc_type==BUNDLED ? "BUNDLED" :
00458           doc_type==OLD_BUNDLED ? "OLD_BUNDLED" :
00459           doc_type==INDIRECT ? "INDIRECT" :
00460           doc_type==OLD_INDEXED ? "OLD_INDEXED" :
00461           doc_type==SINGLE_PAGE ? "SINGLE_PAGE" :
00462           "UNKNOWN") << "'\n");
00463 }
00464 
00465 bool
00466 DjVuDocument::wait_for_complete_init(void)
00467 {
00468   flags.enter();
00469   while(!(flags & DOC_INIT_FAILED) &&
00470         !(flags & DOC_INIT_OK)) flags.wait();
00471   flags.leave();
00472   init_thread_flags.enter();
00473   while (!(init_thread_flags & FINISHED))
00474     init_thread_flags.wait();
00475   init_thread_flags.leave();
00476   return (flags & (DOC_INIT_OK | DOC_INIT_FAILED))!=0;
00477 }
00478 
00479 int
00480 DjVuDocument::wait_get_pages_num(void) const
00481 {
00482   GSafeFlags &f=const_cast<GSafeFlags &>(flags);
00483   f.enter();
00484   while(!(f & DOC_TYPE_KNOWN) &&
00485         !(f & DOC_INIT_FAILED) &&
00486         !(f & DOC_INIT_OK)) f.wait();
00487   f.leave();
00488   return get_pages_num();
00489 }
00490 
00491 GUTF8String
00492 DjVuDocument::get_int_prefix(void) const
00493 {
00494       
00495       
00496       
00497       
00498       
00499       
00500    GUTF8String retval;
00501    return retval.format("document_%p%d?", this, hash(init_url));
00502 }
00503 
00504 void
00505 DjVuDocument::set_file_aliases(const DjVuFile * file)
00506 {
00507    DEBUG_MSG("DjVuDocument::set_file_aliases(): setting global aliases for file '"
00508          << file->get_url() << "'\n");
00509    DEBUG_MAKE_INDENT(3);
00510 
00511    DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
00512    
00513    GMonitorLock lock(&((DjVuFile *) file)->get_safe_flags());
00514    pcaster->clear_aliases(file);
00515    if (file->is_decode_ok() && cache)
00516    {
00517      
00518      
00519      
00520       
00521       pcaster->add_alias(file, file->get_url().get_string());
00522       if (flags & (DOC_NDIR_KNOWN | DOC_DIR_KNOWN))
00523       {
00524      int page_num=url_to_page(file->get_url());
00525      if (page_num>=0)
00526      {
00527         if (page_num==0) pcaster->add_alias(file, init_url.get_string()+"#-1");
00528         pcaster->add_alias(file, init_url.get_string()+"#"+GUTF8String(page_num));
00529      }
00530       }
00531      
00532      
00533      
00534       pcaster->add_alias(file, file->get_url().get_string()+"#-1");
00535    } else pcaster->add_alias(file, get_int_prefix()+file->get_url());
00536 }
00537 
00538 void
00539 DjVuDocument::check_unnamed_files(void)
00540 {
00541   DEBUG_MSG("DjVuDocument::check_unnamed_files(): Seeing if we can fix some...\n");
00542   DEBUG_MAKE_INDENT(3);
00543   
00544   if (flags & DOC_INIT_FAILED)
00545   {
00546     
00547     GCriticalSectionLock lock(&ufiles_lock);
00548     for(GPosition pos=ufiles_list;pos;++pos)
00549     {
00550       GP<DjVuFile> file=ufiles_list[pos]->file;
00551       file->stop_decode(true);
00552       file->stop(false);    
00553     }
00554     ufiles_list.empty();
00555     return;
00556   }
00557   
00558   if ((flags & DOC_TYPE_KNOWN)==0)
00559     return;
00560   
00561   
00562   
00563   
00564   while(true)
00565   {
00566     DjVuPortcaster * pcaster=get_portcaster();
00567     
00568     GP<UnnamedFile> ufile;
00569     GURL new_url;
00570     GPosition pos ;   
00571        GCriticalSectionLock lock(&ufiles_lock);
00572      for(pos=ufiles_list;pos;)
00573      {
00574     G_TRY
00575         {
00576           GP<UnnamedFile> f=ufiles_list[pos];
00577           if (f->id_type==UnnamedFile::ID) 
00578             new_url=id_to_url(f->id);
00579           else 
00580             new_url=page_to_url(f->page_num);
00581           if (!new_url.is_empty())
00582           {
00583             ufile=f;
00584             
00585             
00586             
00587             
00588             break;
00589           } else if (is_init_complete())
00590           {
00591             
00592             
00593             
00594             f->data_pool->set_eof();
00595             GUTF8String msg;
00596             if (f->id_type==UnnamedFile::ID)
00597               msg= ERR_MSG("DjVuDocument.miss_page_name") "\t"+f->id;
00598             else 
00599               msg= ERR_MSG("DjVuDocument.miss_page_num") "\t"+GUTF8String(f->page_num);
00600             G_THROW(msg);
00601           }
00602           ++pos;
00603         }
00604         G_CATCH(exc)
00605         {
00606           pcaster->notify_error(this, exc.get_cause());
00607           GP<DataPool> pool=ufiles_list[pos]->data_pool;
00608           if (pool)
00609             pool->stop();
00610           GPosition this_pos=pos;
00611           ++pos;
00612           ufiles_list.del(this_pos);
00613         }
00614         G_ENDCATCH;
00615      }
00616      
00617      if (ufile && !new_url.is_empty())
00618        {
00619          DEBUG_MSG("Fixing file: '" << ufile->url << "'=>'" << new_url << "'\n");
00620          
00621          
00622          
00623          
00624          
00625          G_TRY
00626            {
00627              if (ufile->data_pool)
00628                {
00629                  GP<DataPool> new_pool=pcaster->request_data(ufile->file, new_url);
00630                  if(!new_pool)
00631                    G_THROW( ERR_MSG("DjVuDocument.fail_URL") "\t"+new_url.get_string());
00632                  ufile->data_pool->connect(new_pool);
00633                }
00634              ufile->file->set_name(new_url.fname());
00635              ufile->file->move(new_url.base());
00636              set_file_aliases(ufile->file);
00637            }
00638          G_CATCH(exc)
00639            {
00640              pcaster->notify_error(this, exc.get_cause());
00641            }   
00642          G_ENDCATCH;
00643        }
00644      else
00645        break;
00646      
00647      
00648      for(pos=ufiles_list;pos;++pos)
00649        if (ufiles_list[pos]==ufile)
00650          {
00651            ufiles_list.del(pos);
00652            break;
00653          }
00654   } 
00655 }
00656 
00657 int
00658 DjVuDocument::get_pages_num(void) const
00659 {
00660    check();
00661    if (flags & DOC_TYPE_KNOWN)
00662       if (doc_type==BUNDLED || doc_type==INDIRECT)
00663      return djvm_dir->get_pages_num();
00664       else if (flags & DOC_NDIR_KNOWN)
00665      return ndir->get_pages_num();
00666    return 1;
00667 }
00668 
00669 GURL
00670 DjVuDocument::page_to_url(int page_num) const
00671 {
00672    check();
00673    DEBUG_MSG("DjVuDocument::page_to_url(): page_num=" << page_num << "\n");
00674    DEBUG_MAKE_INDENT(3);
00675    
00676    GURL url;
00677    if (flags & DOC_TYPE_KNOWN)
00678       switch(doc_type)
00679       {
00680      case SINGLE_PAGE:
00681      case OLD_INDEXED:
00682      {
00683         if (page_num<0) url=init_url;
00684         else if (flags & DOC_NDIR_KNOWN) url=ndir->page_to_url(page_num);
00685         break;
00686      }
00687      case OLD_BUNDLED:
00688      {
00689         if (page_num<0) page_num=0;
00690         if (page_num==0 && (flags & DOC_DIR_KNOWN))
00691            url=GURL::UTF8(first_page_name,init_url);
00692         else if (flags & DOC_NDIR_KNOWN)
00693            url=ndir->page_to_url(page_num);
00694         break;
00695      }
00696      case BUNDLED:
00697      {
00698         if (page_num<0)
00699               page_num=0;
00700         if (flags & DOC_DIR_KNOWN)
00701         {
00702           GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num);
00703           if (!file) G_THROW( ERR_MSG("DjVuDocument.big_num") );
00704           url=GURL::UTF8(file->get_load_name(),init_url);
00705         }
00706         break;
00707      }
00708      case INDIRECT:
00709      {
00710         if (page_num<0) page_num=0;
00711         if (flags & DOC_DIR_KNOWN)
00712         {
00713            GP<DjVmDir::File> file=djvm_dir->page_to_file(page_num);
00714            if (!file)
00715                  G_THROW( ERR_MSG("DjVuDocument.big_num") );
00716            url=GURL::UTF8(file->get_load_name(),init_url.base());
00717         }
00718         break;
00719      }
00720      default:
00721         G_THROW( ERR_MSG("DjVuDocument.unk_type") );
00722       }
00723    return url;
00724 }
00725 
00726 int
00727 DjVuDocument::url_to_page(const GURL & url) const
00728 {
00729    check();
00730    DEBUG_MSG("DjVuDocument::url_to_page(): url='" << url << "'\n");
00731    DEBUG_MAKE_INDENT(3);
00732 
00733    int page_num=-1;
00734    if (flags & DOC_TYPE_KNOWN)
00735       switch(doc_type)
00736       {
00737      case SINGLE_PAGE:
00738      case OLD_BUNDLED:
00739      case OLD_INDEXED:
00740      {
00741         if (flags & DOC_NDIR_KNOWN) page_num=ndir->url_to_page(url);
00742         break;
00743      }
00744      case BUNDLED:
00745      {
00746         if (flags & DOC_DIR_KNOWN)
00747         {
00748            GP<DjVmDir::File> file;
00749            if (url.base()==init_url)
00750                  file=djvm_dir->id_to_file(url.fname());
00751            if (file)
00752                  page_num=file->get_page_num();
00753         }
00754         break;
00755      }
00756      case INDIRECT:
00757      {
00758         if (flags & DOC_DIR_KNOWN)
00759         {
00760            GP<DjVmDir::File> file;
00761            if (url.base()==init_url.base())
00762                  file=djvm_dir->id_to_file(url.fname());
00763            if (file)
00764                  page_num=file->get_page_num();
00765         }
00766         break;
00767      }
00768      default:
00769         G_THROW( ERR_MSG("DjVuDocument.unk_type") );
00770       }
00771    return page_num;
00772 }
00773 
00774 GURL
00775 DjVuDocument::id_to_url(const GUTF8String & id) const
00776 {
00777    check();
00778    DEBUG_MSG("DjVuDocument::id_to_url(): translating ID='" << id << "' to URL\n");
00779    DEBUG_MAKE_INDENT(3);
00780 
00781    if (flags & DOC_TYPE_KNOWN)
00782       switch(doc_type)
00783       {
00784      case BUNDLED:
00785         if (flags & DOC_DIR_KNOWN)
00786         {
00787           GP<DjVmDir::File> file=djvm_dir->id_to_file(id);
00788           if (!file)
00789               {
00790                 file=djvm_dir->name_to_file(id);
00791             if (!file)
00792                   file=djvm_dir->title_to_file(id);
00793               }
00794           if (file)
00795             return GURL::UTF8(file->get_load_name(),init_url);
00796         }
00797         break;
00798      case INDIRECT:
00799         if (flags & DOC_DIR_KNOWN)
00800         {
00801            GP<DjVmDir::File> file=djvm_dir->id_to_file(id);
00802            if (!file)
00803                {
00804                  file=djvm_dir->name_to_file(id);
00805              if (!file)
00806                    file=djvm_dir->title_to_file(id);
00807                }
00808            if (file)
00809              return GURL::UTF8(file->get_load_name(),init_url.base());
00810         }
00811         break;
00812      case OLD_BUNDLED:
00813         if (flags & DOC_DIR_KNOWN)
00814         {
00815            GP<DjVmDir0::FileRec> frec=djvm_dir0->get_file(id);
00816            if (frec)
00817                  return GURL::UTF8(id,init_url);
00818         }
00819         break;
00820      case OLD_INDEXED:
00821      case SINGLE_PAGE:
00822         return GURL::UTF8(id,init_url.base());
00823         break;
00824       }
00825    return GURL();
00826 }
00827 
00828 GURL
00829 DjVuDocument::id_to_url(const DjVuPort * source, const GUTF8String &id)
00830 {
00831    return id_to_url(id);
00832 }
00833 
00834 GP<DjVuFile>
00835 DjVuDocument::url_to_file(const GURL & url, bool dont_create) const
00836       
00837       
00838 {
00839    check();
00840    DEBUG_MSG("DjVuDocument::url_to_file(): url='" << url << "'\n");
00841    DEBUG_MAKE_INDENT(3);
00842 
00843       
00844    DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
00845    GP<DjVuPort> port;
00846 
00847    if (cache)
00848    {
00849      
00850       port=pcaster->alias_to_port(url.get_string());
00851       if (port && port->inherits("DjVuFile"))
00852       {
00853      DEBUG_MSG("found fully decoded file using DjVuPortcaster\n");
00854      return (DjVuFile *) (DjVuPort *) port;
00855       }
00856    }
00857 
00858       
00859    port=pcaster->alias_to_port(get_int_prefix()+url);
00860    if (port && port->inherits("DjVuFile"))
00861    {
00862       DEBUG_MSG("found internal file using DjVuPortcaster\n");
00863       return (DjVuFile *) (DjVuPort *) port;
00864    }
00865 
00866    GP<DjVuFile> file;
00867    
00868    if (!dont_create)
00869    {
00870       DEBUG_MSG("creating a new file\n");
00871       file=DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof);
00872       const_cast<DjVuDocument *>(this)->set_file_aliases(file);
00873    }
00874 
00875    return file;
00876 }
00877 
00878 GP<DjVuFile>
00879 DjVuDocument::get_djvu_file(int page_num, bool dont_create) const
00880 {
00881    check();
00882    DEBUG_MSG("DjVuDocument::get_djvu_file(): request for page " << page_num << "\n");
00883    DEBUG_MAKE_INDENT(3);
00884 
00885    DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
00886    
00887    GURL url;
00888    {
00889      
00890      
00891      
00892      
00893       GMonitorLock lock(&(const_cast<DjVuDocument *>(this)->flags));
00894       url=page_to_url(page_num);
00895       if (url.is_empty())
00896       {
00897         
00898         
00899         
00900      if (is_init_complete()) return 0;
00901      
00902      DEBUG_MSG("Structure is not known => check <doc_url>#<page_num> alias...\n");
00903      GP<DjVuPort> port;
00904      if (cache)
00905         port=pcaster->alias_to_port(init_url.get_string()+"#"+GUTF8String(page_num));
00906      if (!port || !port->inherits("DjVuFile"))
00907      {
00908         DEBUG_MSG("failed => invent dummy URL and proceed\n");
00909      
00910            
00911            
00912            
00913             GUTF8String name("page");
00914             name+=GUTF8String(page_num);
00915             name+=".djvu";
00916             url=invent_url(name);
00917 
00918             GCriticalSectionLock(&(const_cast<DjVuDocument *>(this)->ufiles_lock));
00919         for(GPosition pos=ufiles_list;pos;++pos)
00920         {
00921            GP<UnnamedFile> f=ufiles_list[pos];
00922            if (f->url==url) return f->file;
00923         }
00924         GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::PAGE_NUM, 0,
00925                           page_num, url, 0);
00926 
00927            
00928            
00929            
00930            
00931            
00932            
00933         const_cast<DjVuDocument *>(this)->ufiles_list.append(ufile);
00934       
00935         GP<DjVuFile> file=
00936               DjVuFile::create(url,const_cast<DjVuDocument *>(this),recover_errors,verbose_eof);
00937         ufile->file=file;
00938         return file;
00939      } else url=((DjVuFile *) (DjVuPort *) port)->get_url();
00940       }
00941    }
00942    
00943    GP<DjVuFile> file=url_to_file(url, dont_create);
00944    if (file) 
00945      pcaster->add_route(file, const_cast<DjVuDocument *>(this));
00946    return file;
00947 }
00948 
00949 GURL
00950 DjVuDocument::invent_url(const GUTF8String &name) const
00951 {
00952    GUTF8String buffer;
00953    buffer.format("djvufileurl://%p/%s", this, (const char *)name);
00954    return GURL::UTF8(buffer);
00955 }
00956 
00957 GP<DjVuFile>
00958 DjVuDocument::get_djvu_file(const GUTF8String& id, bool dont_create)
00959 {
00960   check();
00961   DEBUG_MSG("DjVuDocument::get_djvu_file(): ID='" << id << "'\n");
00962   DEBUG_MAKE_INDENT(3);
00963   if (!id.length())
00964     return get_djvu_file(-1);
00965 
00966 
00967 
00968 
00969   
00970   GURL url;
00971   
00972   
00973   
00974   
00975   {
00976     GMonitorLock lock(&flags);
00977     url=id_to_url(id);
00978     if(url.is_empty() && !id.is_int())
00979     {
00980       
00981       
00982       
00983       if (is_init_complete())
00984         return 0;
00985       
00986       
00987       
00988       url=invent_url(id);
00989       DEBUG_MSG("Invented url='" << url << "'\n");
00990 
00991       GCriticalSectionLock lock(&ufiles_lock);
00992       for(GPosition pos=ufiles_list;pos;++pos)
00993       {
00994         GP<UnnamedFile> f=ufiles_list[pos];
00995         if (f->url==url)
00996           return f->file;
00997       }
00998       GP<UnnamedFile> ufile=new UnnamedFile(UnnamedFile::ID, id, 0, url, 0);
00999 
01000       
01001       
01002       
01003       
01004       
01005       
01006       ufiles_list.append(ufile);
01007       
01008       GP<DjVuFile> file=DjVuFile::create(url,this,recover_errors,verbose_eof);
01009       ufile->file=file;
01010       return file;
01011     }
01012   }
01013      
01014   return get_djvu_file(url,dont_create);
01015 }
01016 
01017 GP<DjVuFile>
01018 DjVuDocument::get_djvu_file(const GURL& url, bool dont_create)
01019 {
01020    check();
01021    DEBUG_MSG("DjVuDocument::get_djvu_file(): URL='" << url << "'\n");
01022    DEBUG_MAKE_INDENT(3);
01023 
01024    if (url.is_empty())
01025      return 0;
01026 
01027    const GP<DjVuFile> file(url_to_file(url, dont_create));
01028 
01029    if (file)
01030      get_portcaster()->add_route(file, this);
01031 
01032    return file;
01033 }
01034 
01035 GP<DjVuImage>
01036 DjVuDocument::get_page(int page_num, bool sync, DjVuPort * port) const
01037 {
01038    check();
01039    DEBUG_MSG("DjVuDocument::get_page(): request for page " << page_num << "\n");
01040    DEBUG_MAKE_INDENT(3);
01041 
01042    GP<DjVuImage> dimg;
01043    const GP<DjVuFile> file(get_djvu_file(page_num));
01044    if (file)
01045    {
01046      dimg=DjVuImage::create(file);
01047      if (port)
01048        DjVuPort::get_portcaster()->add_route(dimg, port);
01049    
01050      file->resume_decode();
01051      if (dimg && sync)
01052        dimg->wait_for_complete_decode();
01053    }
01054    return dimg;
01055 }
01056 
01057 GP<DjVuImage>
01058 DjVuDocument::get_page(const GUTF8String &id, bool sync, DjVuPort * port)
01059 {
01060    check();
01061    DEBUG_MSG("DjVuDocument::get_page(): ID='" << id << "'\n");
01062    DEBUG_MAKE_INDENT(3);
01063 
01064    GP<DjVuImage> dimg;
01065    const GP<DjVuFile> file(get_djvu_file(id));
01066    if(file)
01067    {
01068      dimg=DjVuImage::create(file);
01069      if (port)
01070        DjVuPort::get_portcaster()->add_route(dimg, port);
01071    
01072      file->resume_decode();
01073      if (dimg && sync)
01074        dimg->wait_for_complete_decode();
01075    }
01076    return dimg;
01077 }
01078 
01079 void
01080 DjVuDocument::process_threqs(void)
01081       
01082 {
01083   GCriticalSectionLock lock(&threqs_lock);
01084   for(GPosition pos=threqs_list;pos;)
01085   {
01086     GP<ThumbReq> req=threqs_list[pos];
01087     bool remove=false;
01088     if (req->thumb_file)
01089     {
01090       G_TRY {
01091            
01092         if (req->thumb_file->is_data_present())
01093         {
01094           
01095           GP<ByteStream> str=req->thumb_file->get_init_data_pool()->get_stream();
01096           GP<IFFByteStream> giff=IFFByteStream::create(str);
01097           IFFByteStream &iff=*giff;
01098           GUTF8String chkid;
01099           if (!iff.get_chunk(chkid) || chkid!="FORM:THUM")
01100             G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );
01101           
01102           for(int i=0;i<req->thumb_chunk;i++)
01103           {
01104             if (!iff.get_chunk(chkid)) 
01105               G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );
01106             iff.close_chunk();
01107           }
01108           if (!iff.get_chunk(chkid) || chkid!="TH44")
01109             G_THROW( ERR_MSG("DjVuDocument.bad_thumb") );
01110           
01111           
01112           char buffer[1024];
01113           int length;
01114           while((length=iff.read(buffer, 1024)))
01115             req->data_pool->add_data(buffer, length);
01116           req->data_pool->set_eof();
01117           
01118           
01119           
01120           add_to_cache(req->thumb_file);
01121           
01122           req->thumb_file=0;
01123           req->image_file=0;
01124           remove=true;
01125         }
01126       } G_CATCH(exc) {
01127         GUTF8String msg= ERR_MSG("DjVuDocument.cant_extract") "\n";
01128         msg+=exc.get_cause();
01129         get_portcaster()->notify_error(this, msg);
01130            
01131         req->image_file=get_djvu_file(req->page_num);
01132         req->thumb_file=0;
01133         req->data_pool->set_eof();
01134         remove=true;
01135       } G_ENDCATCH;
01136     } 
01137     
01138     if (req->image_file)
01139     {
01140       G_TRY {
01141            
01142         GSafeFlags & file_flags=req->image_file->get_safe_flags();
01143         {
01144           GMonitorLock lock(&file_flags);
01145           if (!req->image_file->is_decoding())
01146           {
01147             if (req->image_file->is_decode_ok())
01148             {
01149               
01150               const GP<DjVuImage> dimg(DjVuImage::create(req->image_file));
01151               
01152               dimg->wait_for_complete_decode();
01153               
01154               int width = 160;
01155               int height = 160;
01156               
01157               if( dimg->get_width() )
01158                 width = dimg->get_width();
01159               if( dimg->get_height() )
01160                 height = dimg->get_height();
01161               
01162               GRect rect(0, 0, 160, height*160/width);
01163               GP<GPixmap> pm=dimg->get_pixmap(rect, rect, thumb_gamma);
01164               if (!pm)
01165               {
01166                 GP<GBitmap> bm=dimg->get_bitmap(rect, rect, sizeof(int));
01167                 if(bm)
01168                   pm=GPixmap::create(*bm);
01169                 else
01170                   pm = GPixmap::create(rect.height(), rect.width(), 
01171                                        &GPixel::WHITE);
01172               }
01173               
01174               
01175               GP<IW44Image> iwpix=IW44Image::create_encode(*pm);
01176               GP<ByteStream> gstr=ByteStream::create();
01177               IWEncoderParms parms;
01178               parms.slices=97;
01179               parms.bytes=0;
01180               parms.decibels=0;
01181               iwpix->encode_chunk(gstr, parms);
01182               TArray<char> data=gstr->get_data();
01183               
01184               req->data_pool->add_data((const char *) data, data.size());
01185               req->data_pool->set_eof();
01186               
01187               req->thumb_file=0;
01188               req->image_file=0;
01189               remove=true;
01190             } else if (req->image_file->is_decode_failed())
01191             {
01192               
01193               req->thumb_file=0;
01194               req->image_file=0;
01195               req->data_pool->set_eof();
01196               remove=true;
01197             } else
01198             {
01199               req->image_file->start_decode();
01200             }
01201           }
01202         }
01203       } G_CATCH(exc) {
01204         GUTF8String msg="Failed to decode thumbnails:\n";
01205         msg+=exc.get_cause();
01206         get_portcaster()->notify_error(this, msg);
01207         
01208            
01209         req->image_file=0;
01210         req->thumb_file=0;
01211         req->data_pool->set_eof();
01212         remove=true;
01213       } G_ENDCATCH;
01214     }
01215     
01216     if (remove)
01217     {
01218       GPosition this_pos=pos;
01219       ++pos;
01220       threqs_list.del(this_pos);
01221     } else ++pos;
01222   }
01223 }
01224 
01225 GP<DjVuDocument::ThumbReq>
01226 DjVuDocument::add_thumb_req(const GP<ThumbReq> & thumb_req)
01227       
01228       
01229       
01230 {
01231    GCriticalSectionLock lock(&threqs_lock);
01232    for(GPosition pos=threqs_list;pos;++pos)
01233    {
01234       GP<ThumbReq> req=threqs_list[pos];
01235       if (req->page_num==thumb_req->page_num)
01236      return req;
01237    }
01238    threqs_list.append(thumb_req);
01239    return thumb_req;
01240 }
01241 
01242 GList<GUTF8String>
01243 DjVuDocument::get_id_list(void)
01244 {
01245   GList<GUTF8String> ids;
01246   if (is_init_complete())
01247   {
01248     if(djvm_dir)
01249     {
01250       GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
01251       for(GPosition pos=files_list;pos;++pos)
01252       {
01253         ids.append(files_list[pos]->get_load_name());
01254       }
01255     }else
01256     {
01257       const int page_num=get_pages_num();
01258       for(int page=0;page<page_num;page++)
01259       { 
01260         ids.append(page_to_url(page).fname());
01261       }
01262     }
01263   }
01264   return ids;
01265 }
01266 
01267 void
01268 DjVuDocument::map_ids(GMap<GUTF8String,void *> &map)
01269 {
01270   GList<GUTF8String> ids=get_id_list();
01271   for(GPosition pos=ids;pos;++pos)
01272   {
01273     map[ids[pos]]=0;
01274   }
01275 }
01276 
01277 GP<DataPool>
01278 DjVuDocument::get_thumbnail(int page_num, bool dont_decode)
01279 {
01280    DEBUG_MSG("DjVuDocument::get_thumbnail(): page_num=" << page_num << "\n");
01281    DEBUG_MAKE_INDENT(3);
01282 
01283    if (!is_init_complete()) return 0;
01284    
01285    {
01286      
01287       GCriticalSectionLock lock(&threqs_lock);
01288       for(GPosition pos=threqs_list;pos;++pos)
01289       {
01290      GP<ThumbReq> req=threqs_list[pos];
01291      if (req->page_num==page_num)
01292         return req->data_pool;  
01293       }
01294    }
01295 
01296       
01297    GP<ThumbReq> thumb_req=new ThumbReq(page_num, DataPool::create());
01298    
01299       
01300    if (get_doc_type()==INDIRECT || get_doc_type()==BUNDLED)
01301    {
01302      
01303       GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
01304       GP<DjVmDir::File> thumb_file;
01305       int thumb_start=0;
01306       int page_cnt=-1;
01307       for(GPosition pos=files_list;pos;++pos)
01308       {
01309      GP<DjVmDir::File> f=files_list[pos];
01310      if (f->is_thumbnails())
01311      {
01312         thumb_file=f;
01313         thumb_start=page_cnt+1;
01314      } else if (f->is_page())
01315          {
01316            page_cnt++;
01317          }
01318      if (page_cnt==page_num) break;
01319       }
01320       if (thumb_file)
01321       {
01322         
01323      thumb_req->thumb_file=get_djvu_file(thumb_file->get_load_name());
01324      thumb_req->thumb_chunk=page_num-thumb_start;
01325      thumb_req=add_thumb_req(thumb_req);
01326      process_threqs();
01327      return thumb_req->data_pool;
01328       }
01329    }
01330 
01331       
01332       
01333       
01334       
01335    GP<DjVuFile> file=get_djvu_file(page_num, dont_decode);
01336    if (file)
01337    {
01338       thumb_req->image_file=file;
01339 
01340      
01341      
01342       GSafeFlags & file_flags=file->get_safe_flags();
01343       {
01344      GMonitorLock lock(&file_flags);
01345      if (thumb_req->image_file->is_decode_ok() || !dont_decode)
01346      {
01347            
01348            
01349         thumb_req=add_thumb_req(thumb_req);
01350         process_threqs();
01351      } else
01352      {
01353            
01354         thumb_req=0;
01355      }
01356       }
01357    } else thumb_req=0;
01358    
01359    if (thumb_req) return thumb_req->data_pool;
01360    else return 0;
01361 }
01362 
01363 static void
01364 add_to_cache(const GP<DjVuFile> & f, GMap<GURL, void *> & map,
01365          DjVuFileCache * cache)
01366 {
01367    GURL url=f->get_url();
01368    DEBUG_MSG("DjVuDocument::add_to_cache(): url='" << url << "'\n");
01369    DEBUG_MAKE_INDENT(3);
01370    
01371    if (!map.contains(url))
01372    {
01373       map[url]=0;
01374       cache->add_file(f);
01375       
01376       GPList<DjVuFile> list;
01377       for(GPosition pos=list;pos;++pos)
01378      add_to_cache(list[pos], map, cache);
01379    }
01380 }
01381 
01382 void
01383 DjVuDocument::add_to_cache(const GP<DjVuFile> & f)
01384 {
01385    if (cache)
01386    {
01387       GMap<GURL, void *> map;
01388       ::add_to_cache(f, map, cache);
01389    }
01390 }
01391 
01392 void
01393 DjVuDocument::notify_file_flags_changed(const DjVuFile * source,
01394                     long set_mask, long clr_mask)
01395 {
01396       
01397       
01398       
01399    if (set_mask & DjVuFile::DECODE_OK)
01400    {
01401       set_file_aliases(source);
01402       if (cache) add_to_cache((DjVuFile *) source);
01403       if(!needs_compression_flag)
01404       {
01405         if(source->needs_compression())
01406         {
01407           can_compress_flag=true;
01408           needs_compression_flag=true;
01409         }else if(source->can_compress())
01410         {
01411           can_compress_flag=true;
01412         }
01413       }
01414       process_threqs();
01415    }
01416    
01417    if (set_mask & DjVuFile::DATA_PRESENT)
01418       process_threqs();     
01419 }
01420 
01421 GP<DjVuFile>
01422 DjVuDocument::id_to_file(const DjVuPort * source, const GUTF8String &id)
01423 {
01424    return (DjVuFile *) get_djvu_file(id);
01425 }
01426 
01427 GP<DataPool>
01428 DjVuDocument::request_data(const DjVuPort * source, const GURL & url)
01429 {
01430    DEBUG_MSG("DjVuDocument::request_data(): seeing if we can do it\n");
01431    DEBUG_MAKE_INDENT(3);
01432 
01433    if (url==init_url)
01434      return init_data_pool;
01435 
01436    check(); 
01437 
01438    {
01439      
01440      
01441      
01442      
01443       GCriticalSectionLock lock(&ufiles_lock);
01444       for(GPosition pos=ufiles_list;pos;++pos)
01445       {
01446      GP<UnnamedFile> f=ufiles_list[pos];
01447      if (f->url==url)
01448      {
01449         DEBUG_MSG("Found tmp unnamed DjVuFile. Return empty DataPool\n");
01450            
01451            
01452         f->data_pool=DataPool::create();
01453         return f->data_pool;
01454      }
01455       }
01456    }
01457 
01458       
01459       
01460    GP<DataPool> data_pool;
01461    if (flags & DOC_TYPE_KNOWN)
01462       switch(doc_type)
01463       {
01464      case OLD_BUNDLED:
01465      {
01466         if (flags & DOC_DIR_KNOWN)
01467         {
01468            DEBUG_MSG("The document is in OLD_BUNDLED format\n");
01469            if (url.base()!=init_url)
01470                 G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"+url.get_string());
01471      
01472            GP<DjVmDir0::FileRec> file=djvm_dir0->get_file(url.fname());
01473            if (!file)
01474                {
01475                  G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname());
01476                }
01477            data_pool=DataPool::create(init_data_pool, file->offset, file->size);
01478         }
01479         break;
01480      }
01481      case BUNDLED:
01482      {
01483         if (flags & DOC_DIR_KNOWN)
01484         {
01485            DEBUG_MSG("The document is in new BUNDLED format\n");
01486            if (url.base()!=init_url)
01487                {
01488          G_THROW( ERR_MSG("DjVuDocument.URL_outside") "\t"
01489                    +url.get_string());
01490                }
01491      
01492            GP<DjVmDir::File> file=djvm_dir->id_to_file(url.fname());
01493            if (!file)
01494                {
01495                  G_THROW( ERR_MSG("DjVuDocument.file_outside") "\t"+url.fname());
01496                }
01497            data_pool=DataPool::create(init_data_pool, file->offset, file->size);
01498         }
01499         break;
01500      }
01501      case SINGLE_PAGE:
01502      case OLD_INDEXED:
01503      case INDIRECT:
01504      {
01505         DEBUG_MSG("The document is in SINGLE_PAGE or OLD_INDEXED or INDIRECT format\n");
01506         if (flags & DOC_DIR_KNOWN)
01507            if (doc_type==INDIRECT && !djvm_dir->id_to_file(url.fname()))
01508                 G_THROW( ERR_MSG("DjVuDocument.URL_outside2") "\t"+url.get_string());
01509      
01510         if (url.is_local_file_url())
01511         {
01512 
01513 
01514            DEBUG_MSG("url=" << url << "\n");
01515 
01516            data_pool=DataPool::create(url);
01517         }
01518      }
01519       }
01520    return data_pool;
01521 }
01522 
01523 
01524 static void
01525 add_file_to_djvm(const GP<DjVuFile> & file, bool page,
01526          DjVmDoc & doc, GMap<GURL, void *> & map)
01527       
01528       
01529       
01530 {
01531    GURL url=file->get_url();
01532 
01533    if (!map.contains(url))
01534    {
01535       map[url]=0;
01536 
01537       if (file->get_chunks_number()>0 && !file->contains_chunk("NDIR"))
01538       {
01539         
01540         
01541         
01542      GPosition pos;
01543      GPList<DjVuFile> files_list=file->get_included_files(false);
01544      GP<DataPool> data=file->get_djvu_data(false);
01545      for(pos=files_list;pos;++pos)
01546      {
01547         GP<DjVuFile> f=files_list[pos];
01548         if (f->contains_chunk("NDIR"))
01549            data=DjVuFile::unlink_file(data, f->get_url().fname());
01550      }
01551      
01552         
01553      GUTF8String name=file->get_url().fname();
01554      GP<DjVmDir::File> file_rec=DjVmDir::File::create(
01555            name, name, name,
01556            page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE );
01557      doc.insert_file(file_rec, data, -1);
01558 
01559         
01560      for(pos=files_list;pos;++pos)
01561         add_file_to_djvm(files_list[pos], false, doc, map);
01562       }
01563    }
01564 }
01565 
01566 static void
01567 add_file_to_djvm(const GP<DjVuFile> & file, bool page,
01568          DjVmDoc & doc, GMap<GURL, void *> & map, 
01569                  bool &needs_compression_flag, bool &can_compress_flag )
01570 {
01571   if(!needs_compression_flag)
01572   {
01573     if(file->needs_compression())
01574     {
01575       can_compress_flag=true;
01576       needs_compression_flag=true;
01577     }else if(file->can_compress())
01578     {
01579       can_compress_flag=true;
01580     }
01581   }
01582   add_file_to_djvm(file,page,doc,map);
01583 }
01584 
01585 static void
01586 local_get_url_names(DjVuFile * f,const GMap<GURL, void *> & map,GMap<GURL,void *> &tmpmap)
01587 {
01588    GURL url=f->get_url();
01589    if (!map.contains(url) && !tmpmap.contains(url))
01590    {
01591       tmpmap[url]=0;
01592       f->process_incl_chunks();
01593       GPList<DjVuFile> files_list=f->get_included_files(false);
01594       for(GPosition pos=files_list;pos;++pos)
01595          local_get_url_names(files_list[pos], map, tmpmap);
01596    }
01597 }
01598 
01599 static void
01600 local_get_url_names(DjVuFile * f, GMap<GURL, void *> & map)
01601 {
01602    GMap<GURL,void *> tmpmap;
01603    local_get_url_names(f,map,tmpmap);
01604    for(GPosition pos=tmpmap;pos;++pos)
01605      map[tmpmap.key(pos)]=0;
01606 }
01607 
01608 GList<GURL>
01609 DjVuDocument::get_url_names(void)
01610 {
01611   check();
01612 
01613   GCriticalSectionLock lock(&url_names_lock);
01614   if(has_url_names)
01615     return url_names;
01616 
01617   GMap<GURL, void *> map;
01618   int i;
01619   if (doc_type==BUNDLED || doc_type==INDIRECT)
01620   {
01621     GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
01622     for(GPosition pos=files_list;pos;++pos)
01623     {
01624       GURL url=id_to_url(files_list[pos]->get_load_name());
01625       map[url]=0;
01626     }
01627   }else
01628   {
01629     int pages_num=get_pages_num();
01630     for(i=0;i<pages_num;i++)
01631     {
01632       G_TRY
01633       {
01634         local_get_url_names(get_djvu_file(i), map);
01635       }
01636       G_CATCH(ex)
01637       {
01638         
01639         G_TRY { 
01640           get_portcaster()->notify_error(this, ex.get_cause()); 
01641           GUTF8String emsg = ERR_MSG("DjVuDocument.exclude_page") "\t" + (i+1);
01642           get_portcaster()->notify_error(this, emsg);
01643         }
01644         G_CATCH_ALL
01645         {
01646           G_RETHROW;
01647         }
01648         G_ENDCATCH;
01649       }
01650       G_ENDCATCH;
01651     }
01652   }
01653   for(GPosition j=map;j;++j)
01654   {
01655     if (map.key(j).is_local_file_url())
01656     {
01657       url_names.append(map.key(j));
01658     }
01659   }
01660   has_url_names=true;
01661   return url_names;
01662 }
01663 
01664 GP<DjVmDoc>
01665 DjVuDocument::get_djvm_doc()
01666       
01667 {
01668    check();
01669    DEBUG_MSG("DjVuDocument::get_djvm_doc(): creating the DjVmDoc\n");
01670    DEBUG_MAKE_INDENT(3);
01671 
01672    if (!is_init_complete())
01673      G_THROW( ERR_MSG("DjVuDocument.init_not_done") );
01674 
01675    GP<DjVmDoc> doc=DjVmDoc::create();
01676 
01677    if (doc_type==BUNDLED || doc_type==INDIRECT)
01678      {
01679        GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
01680        for(GPosition pos=files_list;pos;++pos)
01681          {
01682            GP<DjVmDir::File> f=new DjVmDir::File(*files_list[pos]);
01683            GP<DjVuFile> file=url_to_file(id_to_url(f->get_load_name()));
01684            GP<DataPool> data;
01685            if (file->is_modified()) 
01686              data=file->get_djvu_data(false);
01687            else 
01688              data=file->get_init_data_pool();
01689            doc->insert_file(f, data);
01690          }
01691        if (djvm_nav)
01692          doc->set_djvm_nav(djvm_nav);
01693      } 
01694    else if (doc_type==SINGLE_PAGE)
01695      {
01696        DEBUG_MSG("Creating: djvm for a single page document.\n");
01697        GMap<GURL, void *> map_add;
01698        GP<DjVuFile> file=get_djvu_file(0);
01699        add_file_to_djvm(file, true, *doc, map_add,
01700                         needs_compression_flag,can_compress_flag);
01701      } 
01702    else
01703      {
01704        DEBUG_MSG("Converting: the document is in an old format.\n");
01705        GMap<GURL, void *> map_add;
01706        if(recover_errors == ABORT)
01707          {
01708            for(int page_num=0;page_num<ndir->get_pages_num();page_num++)
01709              {
01710                GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num));
01711                add_file_to_djvm(file, true, *doc, map_add,
01712                                 needs_compression_flag,can_compress_flag);
01713              }
01714          }
01715        else
01716          {
01717            for(int page_num=0;page_num<ndir->get_pages_num();page_num++)
01718              {
01719                G_TRY
01720                  {
01721                    GP<DjVuFile> file=url_to_file(ndir->page_to_url(page_num));
01722                    add_file_to_djvm(file, true, *doc, map_add,
01723                                     needs_compression_flag,can_compress_flag);
01724                  }
01725                G_CATCH(ex)
01726                  {
01727                    G_TRY { 
01728                      get_portcaster()->notify_error(this, ex.get_cause());
01729                      GUTF8String emsg = ERR_MSG("DjVuDocument.skip_page") "\t" 
01730                                       + (page_num+1);
01731                      get_portcaster()->notify_error(this, emsg);
01732                    }
01733                    G_CATCH_ALL
01734                      {
01735                        G_RETHROW;
01736                      }
01737                    G_ENDCATCH;
01738                  }
01739                G_ENDCATCH;
01740              }
01741          }
01742      }
01743    return doc;
01744 }
01745 
01746 void
01747 DjVuDocument::write( const GP<ByteStream> &gstr,
01748   const GMap<GUTF8String,void *> &reserved)
01749 {
01750   DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n");
01751   DEBUG_MAKE_INDENT(3);
01752   get_djvm_doc()->write(gstr,reserved); 
01753 }
01754 
01755 void
01756 DjVuDocument::write(const GP<ByteStream> &gstr, bool force_djvm)
01757 {
01758   DEBUG_MSG("DjVuDocument::write(): storing DjVmDoc into ByteStream\n");
01759   DEBUG_MAKE_INDENT(3);
01760    
01761   GP<DjVmDoc> doc=get_djvm_doc();
01762   GP<DjVmDir> dir=doc->get_djvm_dir();
01763   if (force_djvm || dir->get_files_num()>1)
01764   {
01765     doc->write(gstr);
01766   }else
01767   {
01768     GPList<DjVmDir::File> files_list=dir->resolve_duplicates(false);
01769     GP<DataPool> pool=doc->get_data(files_list[files_list]->get_load_name());
01770     GP<ByteStream> pool_str=pool->get_stream();
01771     ByteStream &str=*gstr;
01772     str.writall(octets,4);
01773     str.copy(*pool_str);
01774   }
01775 }
01776 
01777 void
01778 DjVuDocument::expand(const GURL &codebase, const GUTF8String &idx_name)
01779 {
01780    DEBUG_MSG("DjVuDocument::expand(): codebase='" << codebase << "'\n");
01781    DEBUG_MAKE_INDENT(3);
01782    
01783    GP<DjVmDoc> doc=get_djvm_doc();
01784    doc->expand(codebase, idx_name);
01785 }
01786 
01787 void
01788 DjVuDocument::save_as(const GURL &where, const bool bundled)
01789 {
01790    DEBUG_MSG("DjVuDocument::save_as(): where='" << where <<
01791          "', bundled=" << bundled << "\n");
01792    DEBUG_MAKE_INDENT(3);
01793    
01794    if (needs_compression())
01795    { 
01796      if(!djvu_compress_codec)
01797      {
01798        G_THROW( ERR_MSG("DjVuDocument.comp_codec") );
01799      }
01800      GP<ByteStream> gmbs=ByteStream::create();
01801      write(gmbs);
01802      ByteStream &mbs=*gmbs;
01803      mbs.flush();
01804      mbs.seek(0,SEEK_SET);
01805      (*djvu_compress_codec)(gmbs,where,bundled);
01806    }else if (bundled)
01807    {
01808       DataPool::load_file(where);
01809       write(ByteStream::create(where, "wb"));
01810    } else 
01811    {
01812      expand(where.base(), where.fname());
01813    }
01814 }
01815 
01816 static const char prolog[]="<?xml version=\"1.0\" ?>\n<!DOCTYPE DjVuXML PUBLIC \"-//W3C//DTD DjVuXML 1.1//EN\" \"pubtext/DjVuXML-s.dtd\">\n<DjVuXML>\n<HEAD>";
01817 static const char start_xml[]="</HEAD>\n<BODY>\n";
01818 static const char end_xml[]="</BODY>\n</DjVuXML>\n";
01819 
01820 void
01821 DjVuDocument::writeDjVuXML(const GP<ByteStream> &gstr_out,int flags) const
01822 {
01823   ByteStream &str_out=*gstr_out;
01824   str_out.writestring(
01825     prolog+get_init_url().get_string().toEscaped()+start_xml);
01826   const int pages=wait_get_pages_num();
01827   for(int page_num=0;page_num<pages;++page_num)
01828   {
01829     const GP<DjVuImage> dimg(get_page(page_num,true));
01830     if(!dimg)
01831     {
01832       G_THROW( ERR_MSG("DjVuToText.decode_failed") );
01833     }
01834     dimg->writeXML(str_out,get_init_url(),flags);
01835   }
01836   str_out.writestring(GUTF8String(end_xml));
01837 }
01838 
01839 
01840 #ifdef HAVE_NAMESPACES
01841 }
01842 # ifndef NOT_USING_DJVU_NAMESPACE
01843 using namespace DJVU;
01844 # endif
01845 #endif