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