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 "DjVuFile.h"
00065 #include "IFFByteStream.h"
00066 #include "GOS.h"
00067 #include "MMRDecoder.h"
00068 #ifdef NEED_JPEG_DECODER
00069 #include "JPEGDecoder.h"
00070 #endif
00071 #include "DjVuAnno.h"
00072 #include "DjVuText.h"
00073 #include "DataPool.h"
00074 #include "JB2Image.h"
00075 #include "IW44Image.h"
00076 #include "DjVuNavDir.h"
00077 #ifndef NEED_DECODER_ONLY
00078 #include "BSByteStream.h"
00079 #endif // NEED_DECODER_ONLY
00080
00081 #include "debug.h"
00082
00083
00084 #ifdef HAVE_NAMESPACES
00085 namespace DJVU {
00086 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00087 }
00088 #endif
00089 #endif
00090
00091
00092 #define STRINGIFY(x) STRINGIFY_(x)
00093 #define STRINGIFY_(x) #x
00094
00095
00096 #define REPORT_EOF(x) \
00097 {G_TRY{G_THROW( ByteStream::EndOfFile );}G_CATCH(ex){report_error(ex,(x));}G_ENDCATCH;}
00098
00099 static GP<GPixmap> (*djvu_decode_codec)(ByteStream &bs)=0;
00100
00101 class ProgressByteStream : public ByteStream
00102 {
00103 public:
00104 ProgressByteStream(const GP<ByteStream> & xstr) : str(xstr),
00105 last_call_pos(0) {}
00106 virtual ~ProgressByteStream() {}
00107
00108 virtual size_t read(void *buffer, size_t size)
00109 {
00110 int rc=0;
00111
00112 G_TRY {
00113 int cur_pos=str->tell();
00114 if (progress_cb && (last_call_pos/256!=cur_pos/256))
00115 {
00116 progress_cb(cur_pos, progress_cl_data);
00117 last_call_pos=cur_pos;
00118 }
00119 rc=str->read(buffer, size);
00120 } G_CATCH_ALL {
00121 G_RETHROW;
00122 } G_ENDCATCH;
00123 return rc;
00124 }
00125 virtual size_t write(const void *buffer, size_t size)
00126 {
00127 return str->write(buffer, size);
00128 }
00129 virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false)
00130 {
00131 return str->seek(offset, whence);
00132 }
00133 virtual long tell(void ) const { return str->tell(); }
00134
00135 void set_progress_cb(void (* xprogress_cb)(int, void *),
00136 void * xprogress_cl_data)
00137 {
00138 progress_cb=xprogress_cb;
00139 progress_cl_data=xprogress_cl_data;
00140 }
00141 private:
00142 GP<ByteStream> str;
00143 void * progress_cl_data;
00144 void (* progress_cb)(int pos, void *);
00145 int last_call_pos;
00146
00147
00148 ProgressByteStream & operator=(const ProgressByteStream &);
00149 };
00150
00151
00152 DjVuFile::DjVuFile()
00153 : file_size(0), recover_errors(ABORT), verbose_eof(false), chunks_number(-1),
00154 initialized(false)
00155 {
00156 }
00157
00158 void
00159 DjVuFile::check() const
00160 {
00161 if (!initialized)
00162 G_THROW( ERR_MSG("DjVuFile.not_init") );
00163 }
00164
00165 GP<DjVuFile>
00166 DjVuFile::create(
00167 const GP<ByteStream> & str, const ErrorRecoveryAction recover_errors,
00168 const bool verbose_eof )
00169 {
00170 DjVuFile *file=new DjVuFile();
00171 GP<DjVuFile> retval=file;
00172 file->set_recover_errors(recover_errors);
00173 file->set_verbose_eof(verbose_eof);
00174 file->init(str);
00175 return retval;
00176 }
00177
00178 void
00179 DjVuFile::init(const GP<ByteStream> & str)
00180 {
00181 DEBUG_MSG("DjVuFile::DjVuFile(): ByteStream constructor\n");
00182 DEBUG_MAKE_INDENT(3);
00183
00184 if (initialized)
00185 G_THROW( ERR_MSG("DjVuFile.2nd_init") );
00186 if (!get_count())
00187 G_THROW( ERR_MSG("DjVuFile.not_secured") );
00188
00189 file_size=0;
00190 decode_thread=0;
00191
00192
00193 data_pool=DataPool::create(str);
00194
00195
00196 GUTF8String buffer;
00197 buffer.format("djvufile:/%p.djvu", this);
00198 DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)buffer<<"\n");
00199 url=GURL::UTF8(buffer);
00200
00201
00202 initialized=true;
00203
00204
00205 data_pool->add_trigger(-1, static_trigger_cb, this);
00206 }
00207
00208 GP<DjVuFile>
00209 DjVuFile::create(
00210 const GURL & xurl, GP<DjVuPort> port,
00211 const ErrorRecoveryAction recover_errors, const bool verbose_eof )
00212 {
00213 DjVuFile *file=new DjVuFile();
00214 GP<DjVuFile> retval=file;
00215 file->set_recover_errors(recover_errors);
00216 file->set_verbose_eof(verbose_eof);
00217 file->init(xurl,port);
00218 return retval;
00219 }
00220
00221 void
00222 DjVuFile::init(const GURL & xurl, GP<DjVuPort> port)
00223 {
00224 DEBUG_MSG("DjVuFile::init(): url='" << xurl << "'\n");
00225 DEBUG_MAKE_INDENT(3);
00226
00227 if (initialized)
00228 G_THROW( ERR_MSG("DjVuFile.2nd_init") );
00229 if (!get_count())
00230 G_THROW( ERR_MSG("DjVuFile.not_secured") );
00231 if (xurl.is_empty())
00232 G_THROW( ERR_MSG("DjVuFile.empty_URL") );
00233
00234 url = xurl;
00235 DEBUG_MSG("DjVuFile::DjVuFile(): url is "<<(const char *)url<<"\n");
00236 file_size=0;
00237 decode_thread=0;
00238
00239 DjVuPortcaster * pcaster=get_portcaster();
00240
00241
00242 pcaster->add_route(this, this);
00243 if (!port)
00244 port = simple_port = new DjVuSimplePort();
00245 pcaster->add_route(this, port);
00246
00247
00248 initialized=true;
00249
00250 if (!(data_pool=DataPool::create(pcaster->request_data(this, url))))
00251 G_THROW( ERR_MSG("DjVuFile.no_data") "\t"+url.get_string());
00252 data_pool->add_trigger(-1, static_trigger_cb, this);
00253 }
00254
00255 DjVuFile::~DjVuFile(void)
00256 {
00257 DEBUG_MSG("DjVuFile::~DjVuFile(): destroying...\n");
00258 DEBUG_MAKE_INDENT(3);
00259
00260
00261
00262 get_portcaster()->del_port(this);
00263
00264
00265
00266 if (data_pool)
00267 data_pool->del_trigger(static_trigger_cb, this);
00268
00269
00270
00271
00272 delete decode_thread; decode_thread=0;
00273 }
00274
00275 void
00276 DjVuFile::reset(void)
00277 {
00278 flags.enter();
00279 info = 0;
00280 anno = 0;
00281 text = 0;
00282 meta = 0;
00283 bg44 = 0;
00284 fgbc = 0;
00285 fgjb = 0;
00286 fgjd = 0;
00287 fgpm = 0;
00288 dir = 0;
00289 description = "";
00290 mimetype = "";
00291 flags=(flags&(ALL_DATA_PRESENT|DECODE_STOPPED|DECODE_FAILED));
00292 flags.leave();
00293 }
00294
00295 unsigned int
00296 DjVuFile::get_memory_usage(void) const
00297 {
00298 unsigned int size=sizeof(*this);
00299 if (info) size+=info->get_memory_usage();
00300 if (bg44) size+=bg44->get_memory_usage();
00301 if (fgjb) size+=fgjb->get_memory_usage();
00302 if (fgpm) size+=fgpm->get_memory_usage();
00303 if (fgbc) size+=fgbc->size()*sizeof(int);
00304 if (anno) size+=anno->size();
00305 if (meta) size+=meta->size();
00306 if (dir) size+=dir->get_memory_usage();
00307 return size;
00308 }
00309
00310 GPList<DjVuFile>
00311 DjVuFile::get_included_files(bool only_created)
00312 {
00313 check();
00314 if (!only_created && !are_incl_files_created())
00315 process_incl_chunks();
00316
00317 GCriticalSectionLock lock(&inc_files_lock);
00318 GPList<DjVuFile> list=inc_files_list;
00319 return list;
00320 }
00321
00322 void
00323 DjVuFile::wait_for_chunk(void)
00324
00325 {
00326 check();
00327 DEBUG_MSG("DjVuFile::wait_for_chunk() called\n");
00328 DEBUG_MAKE_INDENT(3);
00329 chunk_mon.enter();
00330 chunk_mon.wait();
00331 chunk_mon.leave();
00332 }
00333
00334 bool
00335 DjVuFile::wait_for_finish(bool self)
00336
00337
00338
00339
00340 {
00341 DEBUG_MSG("DjVuFile::wait_for_finish(): self=" << self <<"\n");
00342 DEBUG_MAKE_INDENT(3);
00343
00344 check();
00345
00346 if (self)
00347 {
00348
00349
00350
00351 GMonitorLock lock(&flags);
00352 if (is_decoding())
00353 {
00354 while(is_decoding()) flags.wait();
00355 DEBUG_MSG("got it\n");
00356 return 1;
00357 }
00358 } else
00359 {
00360
00361
00362
00363
00364
00365
00366 GMonitorLock lock(&finish_mon);
00367 GP<DjVuFile> file;
00368 {
00369 GCriticalSectionLock lock(&inc_files_lock);
00370 for(GPosition pos=inc_files_list;pos;++pos)
00371 {
00372 GP<DjVuFile> & f=inc_files_list[pos];
00373 if (f->is_decoding())
00374 {
00375 file=f; break;
00376 }
00377 }
00378 }
00379 if (file)
00380 {
00381 finish_mon.wait();
00382 DEBUG_MSG("got it\n");
00383 return 1;
00384 }
00385 }
00386 DEBUG_MSG("nothing to wait for\n");
00387 return 0;
00388 }
00389
00390 void
00391 DjVuFile::notify_chunk_done(const DjVuPort *, const GUTF8String &)
00392 {
00393 check();
00394 chunk_mon.enter();
00395 chunk_mon.broadcast();
00396 chunk_mon.leave();
00397 }
00398
00399 void
00400 DjVuFile::notify_file_flags_changed(const DjVuFile * src,
00401 long set_mask, long clr_mask)
00402 {
00403 check();
00404 if (set_mask & (DECODE_OK | DECODE_FAILED | DECODE_STOPPED))
00405 {
00406
00407 finish_mon.enter();
00408 finish_mon.broadcast();
00409 finish_mon.leave();
00410
00411
00412 chunk_mon.enter();
00413 chunk_mon.broadcast();
00414 chunk_mon.leave();
00415 }
00416
00417 if ((set_mask & ALL_DATA_PRESENT) && src!=this &&
00418 are_incl_files_created() && is_data_present())
00419 {
00420 if (src!=this && are_incl_files_created() && is_data_present())
00421 {
00422
00423 bool all=true;
00424 {
00425 GCriticalSectionLock lock(&inc_files_lock);
00426 for(GPosition pos=inc_files_list;pos;++pos)
00427 if (!inc_files_list[pos]->is_all_data_present())
00428 {
00429 all=false;
00430 break;
00431 }
00432 }
00433 if (all)
00434 {
00435 DEBUG_MSG("Just got ALL data for '" << url << "'\n");
00436 flags|=ALL_DATA_PRESENT;
00437 get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
00438 }
00439 }
00440 }
00441 }
00442
00443 void
00444 DjVuFile::static_decode_func(void * cl_data)
00445 {
00446 DjVuFile * th=(DjVuFile *) cl_data;
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462 GP<DjVuFile> life_saver=th;
00463 th->decode_life_saver=0;
00464 G_TRY {
00465 th->decode_func();
00466 } G_CATCH_ALL {
00467 } G_ENDCATCH;
00468 }
00469
00470 void
00471 DjVuFile::decode_func(void)
00472 {
00473 check();
00474 DEBUG_MSG("DjVuFile::decode_func() called, url='" << url << "'\n");
00475 DEBUG_MAKE_INDENT(3);
00476
00477 DjVuPortcaster * pcaster=get_portcaster();
00478
00479 G_TRY {
00480 const GP<ByteStream> decode_stream(decode_data_pool->get_stream());
00481 ProgressByteStream *pstr=new ProgressByteStream(decode_stream);
00482 const GP<ByteStream> gpstr(pstr);
00483 pstr->set_progress_cb(progress_cb, this);
00484
00485 decode(gpstr);
00486
00487
00488 while(wait_for_finish(0))
00489 continue;
00490
00491 DEBUG_MSG("waiting for children termination\n");
00492
00493 GCriticalSectionLock lock(&inc_files_lock);
00494 for(GPosition pos=inc_files_list;pos;++pos)
00495 {
00496 GP<DjVuFile> & f=inc_files_list[pos];
00497 if (f->is_decode_failed())
00498 G_THROW( ERR_MSG("DjVuFile.decode_fail") );
00499 if (f->is_decode_stopped())
00500 G_THROW( DataPool::Stop );
00501 if (!f->is_decode_ok())
00502 {
00503 DEBUG_MSG("this_url='" << url << "'\n");
00504 DEBUG_MSG("incl_url='" << f->get_url() << "'\n");
00505 DEBUG_MSG("decoding=" << f->is_decoding() << "\n");
00506 DEBUG_MSG("status='" << f->get_flags() << "\n");
00507 G_THROW( ERR_MSG("DjVuFile.not_finished") );
00508 }
00509 }
00510 } G_CATCH(exc) {
00511 G_TRY {
00512 if (!exc.cmp_cause(DataPool::Stop))
00513 {
00514 flags.enter();
00515 flags=flags & ~DECODING | DECODE_STOPPED;
00516 flags.leave();
00517 pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.stopped"))
00518 + GUTF8String("\t") + GUTF8String(url));
00519 pcaster->notify_file_flags_changed(this, DECODE_STOPPED, DECODING);
00520 } else
00521 {
00522 flags.enter();
00523 flags=flags & ~DECODING | DECODE_FAILED;
00524 flags.leave();
00525 pcaster->notify_status(this, GUTF8String(ERR_MSG("DjVuFile.failed"))
00526 + GUTF8String("\t") + GUTF8String(url));
00527 pcaster->notify_error(this, exc.get_cause());
00528 pcaster->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
00529 }
00530 } G_CATCH_ALL
00531 {
00532 DEBUG_MSG("******* Oops. Almost missed an exception\n");
00533 } G_ENDCATCH;
00534 } G_ENDCATCH;
00535
00536 decode_data_pool->clear_stream();
00537 G_TRY {
00538 if (flags.test_and_modify(DECODING, 0, DECODE_OK | INCL_FILES_CREATED, DECODING))
00539 pcaster->notify_file_flags_changed(this, DECODE_OK | INCL_FILES_CREATED,
00540 DECODING);
00541 } G_CATCH_ALL {} G_ENDCATCH;
00542 DEBUG_MSG("decoding thread for url='" << url << "' ended\n");
00543 }
00544
00545 GP<DjVuFile>
00546 DjVuFile::process_incl_chunk(ByteStream & str, int file_num)
00547 {
00548 check();
00549 DEBUG_MSG("DjVuFile::process_incl_chunk(): processing INCL chunk...\n");
00550 DEBUG_MAKE_INDENT(3);
00551
00552 DjVuPortcaster * pcaster=get_portcaster();
00553
00554 GUTF8String incl_str;
00555 char buffer[1024];
00556 int length;
00557 while((length=str.read(buffer, 1024)))
00558 incl_str+=GUTF8String(buffer, length);
00559
00560
00561 while(incl_str.length() && incl_str[0]=='\n')
00562 {
00563 incl_str=incl_str.substr(1,(unsigned int)(-1));
00564 }
00565 while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
00566 {
00567 incl_str.setat(incl_str.length()-1, 0);
00568 }
00569
00570 if (incl_str.length()>0)
00571 {
00572 if (strchr(incl_str, '/'))
00573 G_THROW( ERR_MSG("DjVuFile.malformed") );
00574
00575 DEBUG_MSG("incl_str='" << incl_str << "'\n");
00576
00577 GURL incl_url=pcaster->id_to_url(this, incl_str);
00578 if (incl_url.is_empty())
00579 incl_url=GURL::UTF8(incl_str,url.base());
00580
00581
00582 {
00583 GCriticalSectionLock lock(&inc_files_lock);
00584 GPosition pos;
00585 for(pos=inc_files_list;pos;++pos)
00586 {
00587 if (inc_files_list[pos]->url.fname()==incl_url.fname())
00588 break;
00589 }
00590 if (pos)
00591 return inc_files_list[pos];
00592 }
00593
00594
00595 GP<DjVuFile> file;
00596 G_TRY
00597 {
00598 file=pcaster->id_to_file(this, incl_str);
00599 }
00600 G_CATCH(ex)
00601 {
00602 unlink_file(incl_str);
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612 get_portcaster()->notify_error(this,ex.get_cause());
00613 return 0;
00614 }
00615 G_ENDCATCH;
00616 if (!file)
00617 {
00618 G_THROW( ERR_MSG("DjVuFile.no_create") "\t"+incl_str);
00619 }
00620 if (recover_errors!=ABORT)
00621 file->set_recover_errors(recover_errors);
00622 if (verbose_eof)
00623 file->set_verbose_eof(verbose_eof);
00624 pcaster->add_route(file, this);
00625
00626
00627 if (flags & STOPPED)
00628 file->stop(false);
00629 if (flags & BLOCKED_STOPPED)
00630 file->stop(true);
00631
00632
00633
00634 {
00635 GCriticalSectionLock lock(&inc_files_lock);
00636 GPosition pos;
00637 for(pos=inc_files_list;pos;++pos)
00638 {
00639 if (inc_files_list[pos]->url.fname()==incl_url.fname())
00640 break;
00641 }
00642 if (pos)
00643 {
00644 file=inc_files_list[pos];
00645 } else if (file_num<0 || !(pos=inc_files_list.nth(file_num)))
00646 {
00647 inc_files_list.append(file);
00648 } else
00649 {
00650 inc_files_list.insert_before(pos, file);
00651 }
00652 }
00653 return file;
00654 }
00655 return 0;
00656 }
00657
00658
00659 void
00660 DjVuFile::report_error(const GException &ex,bool throw_errors)
00661 {
00662 data_pool->clear_stream();
00663 if((!verbose_eof)|| (ex.cmp_cause(ByteStream::EndOfFile)))
00664 {
00665 if(throw_errors)
00666 {
00667 G_EXTHROW(ex);
00668 }else
00669 {
00670 get_portcaster()->notify_error(this,ex.get_cause());
00671 }
00672 }else
00673 {
00674 GURL url=get_url();
00675 GUTF8String url_str=url.get_string();
00676
00677
00678
00679 GUTF8String msg = GUTF8String( ERR_MSG("DjVuFile.EOF") "\t") + url;
00680 if(throw_errors)
00681 {
00682 G_EXTHROW(ex, msg);
00683 }else
00684 {
00685 get_portcaster()->notify_error(this,msg);
00686 }
00687 }
00688 }
00689
00690 void
00691 DjVuFile::process_incl_chunks(void)
00692
00693
00694
00695
00696 {
00697 DEBUG_MSG("DjVuFile::process_incl_chunks(void)\n");
00698 DEBUG_MAKE_INDENT(3);
00699 check();
00700
00701 int incl_cnt=0;
00702
00703 const GP<ByteStream> str(data_pool->get_stream());
00704 GUTF8String chkid;
00705 const GP<IFFByteStream> giff(IFFByteStream::create(str));
00706 IFFByteStream &iff=*giff;
00707 if (iff.get_chunk(chkid))
00708 {
00709 int chunks=0;
00710 int last_chunk=0;
00711 G_TRY
00712 {
00713 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
00714 int chksize;
00715 for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
00716 {
00717 chunks++;
00718 if (chkid=="INCL")
00719 {
00720 G_TRY
00721 {
00722 process_incl_chunk(*iff.get_bytestream(), incl_cnt++);
00723 }
00724 G_CATCH(ex);
00725 {
00726 report_error(ex,(recover_errors <= SKIP_PAGES));
00727 }
00728 G_ENDCATCH;
00729 }else if(chkid=="FAKE")
00730 {
00731 set_needs_compression(true);
00732 set_can_compress(true);
00733 }else if(chkid=="BGjp")
00734 {
00735 set_can_compress(true);
00736 }else if(chkid=="Smmr")
00737 {
00738 set_can_compress(true);
00739 }
00740 iff.seek_close_chunk();
00741 }
00742 if (chunks_number < 0) chunks_number=last_chunk;
00743 }
00744 G_CATCH(ex)
00745 {
00746 if (chunks_number < 0)
00747 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
00748 report_error(ex,(recover_errors <= SKIP_PAGES));
00749 }
00750 G_ENDCATCH;
00751 }
00752 flags|=INCL_FILES_CREATED;
00753 data_pool->clear_stream();
00754 }
00755
00756 GP<JB2Dict>
00757 DjVuFile::static_get_fgjd(void *arg)
00758 {
00759 DjVuFile *file = (DjVuFile*)arg;
00760 return file->get_fgjd(1);
00761 }
00762
00763 GP<JB2Dict>
00764 DjVuFile::get_fgjd(int block)
00765 {
00766 check();
00767
00768
00769 if (fgjd)
00770 return fgjd;
00771
00772 chunk_mon.enter();
00773 G_TRY {
00774 for(;;)
00775 {
00776 int active = 0;
00777 GPList<DjVuFile> incs = get_included_files();
00778 for (GPosition pos=incs.firstpos(); pos; ++pos)
00779 {
00780 GP<DjVuFile> file = incs[pos];
00781 if (file->is_decoding())
00782 active = 1;
00783 GP<JB2Dict> fgjd = file->get_fgjd();
00784 if (fgjd)
00785 {
00786 chunk_mon.leave();
00787 return fgjd;
00788 }
00789 }
00790
00791 if (! block)
00792 break;
00793
00794 if (! active)
00795 break;
00796
00797 wait_for_chunk();
00798 }
00799 } G_CATCH_ALL {
00800 chunk_mon.leave();
00801 G_RETHROW;
00802 } G_ENDCATCH;
00803 chunk_mon.leave();
00804 if (is_decode_stopped()) G_THROW( DataPool::Stop );
00805 return 0;
00806 }
00807
00808 int
00809 DjVuFile::get_dpi(int w, int h)
00810 {
00811 int dpi=0, red=1;
00812 if (info)
00813 {
00814 for(red=1; red<=12; red++)
00815 if ((info->width+red-1)/red==w)
00816 if ((info->height+red-1)/red==h)
00817 break;
00818 if (red>12)
00819 G_THROW( ERR_MSG("DjVuFile.corrupt_BG44") );
00820 dpi=info->dpi;
00821 }
00822 return (dpi ? dpi : 300)/red;
00823 }
00824
00825 static inline bool
00826 is_info(const GUTF8String &chkid)
00827 {
00828 return (chkid=="INFO");
00829 }
00830
00831 static inline bool
00832 is_annotation(const GUTF8String &chkid)
00833 {
00834 return (chkid=="ANTa" ||
00835 chkid=="ANTz" ||
00836 chkid=="FORM:ANNO" );
00837 }
00838
00839 static inline bool
00840 is_text(const GUTF8String &chkid)
00841 {
00842 return (chkid=="TXTa" || chkid=="TXTz");
00843 }
00844
00845 static inline bool
00846 is_meta(const GUTF8String &chkid)
00847 {
00848 return (chkid=="METa" || chkid=="METz");
00849 }
00850
00851
00852 GUTF8String
00853 DjVuFile::decode_chunk( const GUTF8String &id, const GP<ByteStream> &gbs,
00854 bool djvi, bool djvu, bool iw44)
00855 {
00856 DEBUG_MSG("DjVuFile::decode_chunk()\n");
00857 ByteStream &bs=*gbs;
00858 check();
00859
00860
00861
00862
00863
00864 if (get_count()==1)
00865 G_THROW( DataPool::Stop );
00866
00867 GUTF8String desc = ERR_MSG("DjVuFile.unrecog_chunk");
00868 GUTF8String chkid = id;
00869 DEBUG_MSG("DjVuFile::decode_chunk() : decoding " << id << "\n");
00870
00871
00872 if (is_info(chkid) && (djvu || djvi))
00873 {
00874 if (info)
00875 G_THROW( ERR_MSG("DjVuFile.corrupt_dupl") );
00876 if (djvi)
00877 G_THROW( ERR_MSG("DjVuFile.corrupt_INFO") );
00878
00879 GP<DjVuInfo> xinfo=DjVuInfo::create();
00880 xinfo->decode(bs);
00881 info = xinfo;
00882 desc.format( ERR_MSG("DjVuFile.page_info") );
00883
00884 if (info->width<0 || info->height<0)
00885 G_THROW( ERR_MSG("DjVuFile.corrupt_zero") );
00886 if (info->version >= DJVUVERSION_TOO_NEW)
00887 G_THROW( ERR_MSG("DjVuFile.new_version") "\t" STRINGIFY(DJVUVERSION_TOO_NEW) );
00888 if(info->compressable)
00889 set_can_compress(true);
00890 }
00891
00892
00893 else if (chkid == "INCL" && (djvi || djvu || iw44))
00894 {
00895 GP<DjVuFile> file=process_incl_chunk(bs);
00896 if (file)
00897 {
00898 int decode_was_already_started = 1;
00899 {
00900 GMonitorLock lock(&file->flags);
00901
00902 if(file->resume_decode())
00903 {
00904 decode_was_already_started = 0;
00905 }
00906 }
00907
00908 if (decode_was_already_started)
00909 {
00910
00911 if (file->is_decode_ok())
00912 get_portcaster()->notify_file_flags_changed(file, DECODE_OK, 0);
00913 else if (file->is_decode_failed())
00914 get_portcaster()->notify_file_flags_changed(file, DECODE_FAILED, 0);
00915 }
00916 desc.format( ERR_MSG("DjVuFile.indir_chunk1") "\t" + file->get_url().fname() );
00917 } else
00918 desc.format( ERR_MSG("DjVuFile.indir_chunk2") );
00919 }
00920
00921
00922 else if (chkid == "Djbz" && (djvu || djvi))
00923 {
00924 if (this->fgjd)
00925 G_THROW( ERR_MSG("DjVuFile.dupl_Dxxx") );
00926 if (this->fgjd)
00927 G_THROW( ERR_MSG("DjVuFile.Dxxx_after_Sxxx") );
00928 GP<JB2Dict> fgjd = JB2Dict::create();
00929 fgjd->decode(gbs);
00930 this->fgjd = fgjd;
00931 desc.format( ERR_MSG("DjVuFile.shape_dict") "\t%d", fgjd->get_shape_count() );
00932 }
00933
00934
00935 else if (chkid=="Sjbz" && (djvu || djvi))
00936 {
00937 if (this->fgjb)
00938 G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
00939 GP<JB2Image> fgjb=JB2Image::create();
00940
00941 if (info && info->version <=18)
00942 fgjb->reproduce_old_bug = true;
00943
00944 fgjb->decode(gbs, static_get_fgjd, (void*)this);
00945 this->fgjb = fgjb;
00946 desc.format( ERR_MSG("DjVuFile.fg_mask") "\t%d\t%d\t%d",
00947 fgjb->get_width(), fgjb->get_height(),
00948 get_dpi(fgjb->get_width(), fgjb->get_height()));
00949 }
00950
00951
00952 else if (chkid=="Smmr" && (djvu || djvi))
00953 {
00954 if (this->fgjb)
00955 G_THROW( ERR_MSG("DjVuFile.dupl_Sxxx") );
00956 set_can_compress(true);
00957 this->fgjb = MMRDecoder::decode(gbs);
00958 desc.format( ERR_MSG("DjVuFile.G4_mask") "\t%d\t%d\t%d",
00959 fgjb->get_width(), fgjb->get_height(),
00960 get_dpi(fgjb->get_width(), fgjb->get_height()));
00961 }
00962
00963
00964 else if (chkid == "BG44" && (djvu || djvi))
00965 {
00966 if (!bg44)
00967 {
00968 if (bgpm)
00969 G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
00970
00971 GP<IW44Image> bg44=IW44Image::create_decode(IW44Image::COLOR);
00972 bg44->decode_chunk(gbs);
00973 this->bg44 = bg44;
00974 desc.format( ERR_MSG("DjVuFile.IW44_bg1") "\t%d\t%d\t%d",
00975 bg44->get_width(), bg44->get_height(),
00976 get_dpi(bg44->get_width(), bg44->get_height()));
00977 }
00978 else
00979 {
00980
00981 GP<IW44Image> bg44 = this->bg44;
00982 bg44->decode_chunk(gbs);
00983 desc.format( ERR_MSG("DjVuFile.IW44_bg2") "\t%d\t%d",
00984 bg44->get_serial(), get_dpi(bg44->get_width(), bg44->get_height()));
00985 }
00986 }
00987
00988
00989 else if (chkid == "FG44" && (djvu || djvu))
00990 {
00991 if (fgpm || fgbc)
00992 G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
00993 GP<IW44Image> gfg44=IW44Image::create_decode(IW44Image::COLOR);
00994 IW44Image &fg44=*gfg44;
00995 fg44.decode_chunk(gbs);
00996 fgpm=fg44.get_pixmap();
00997 desc.format( ERR_MSG("DjVuFile.IW44_fg") "\t%d\t%d\t%d",
00998 fg44.get_width(), fg44.get_height(),
00999 get_dpi(fg44.get_width(), fg44.get_height()));
01000 }
01001
01002
01003 else if (chkid == "LINK" && (djvu || djvi))
01004 {
01005 if (bg44 || bgpm)
01006 G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01007 if(djvu_decode_codec)
01008 {
01009 set_modified(true);
01010 set_can_compress(true);
01011 set_needs_compression(true);
01012 this->bgpm = djvu_decode_codec(bs);
01013 desc.format( ERR_MSG("DjVuFile.color_import1") "\t%d\t%d\t%d",
01014 bgpm->columns(), bgpm->rows(),
01015 get_dpi(bgpm->columns(), bgpm->rows()));
01016 }else
01017 {
01018 desc.format( ERR_MSG("DjVuFile.color_import2") );
01019 }
01020 }
01021
01022
01023 else if (chkid == "BGjp" && (djvu || djvi))
01024 {
01025 if (bg44 || bgpm)
01026 G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01027 set_can_compress(true);
01028 #ifdef NEED_JPEG_DECODER
01029 this->bgpm = JPEGDecoder::decode(bs);
01030 desc.format( ERR_MSG("DjVuFile.JPEG_bg1") "\t%d\t%d\t%d",
01031 bgpm->columns(), bgpm->rows(),
01032 get_dpi(bgpm->columns(), bgpm->rows()));
01033 #else
01034 desc.format( ERR_MSG("DjVuFile.JPEG_bg2") );
01035 #endif
01036 }
01037
01038
01039 else if (chkid == "FGjp" && (djvu || djvi))
01040 {
01041 if (fgpm || fgbc)
01042 G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01043 #ifdef NEED_JPEG_DECODER
01044 this->fgpm = JPEGDecoder::decode(bs);
01045 desc.format( ERR_MSG("DjVuFile.JPEG_fg1") "\t%d\t%d\t%d",
01046 fgpm->columns(), fgpm->rows(),
01047 get_dpi(fgpm->columns(), fgpm->rows()));
01048 #else
01049 desc.format( ERR_MSG("DjVuFile.JPEG_fg2") );
01050 #endif
01051 }
01052
01053
01054 else if (chkid == "BG2k" && (djvu || djvi))
01055 {
01056 if (bg44)
01057 G_THROW( ERR_MSG("DjVuFile.dupl_backgrnd") );
01058 desc.format( ERR_MSG("DjVuFile.JPEG2K_bg") );
01059 }
01060
01061
01062 else if (chkid == "FG2k" && (djvu || djvi))
01063 {
01064 if (fgpm || fgbc)
01065 G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01066 desc.format( ERR_MSG("DjVuFile.JPEG2K_fg") );
01067 }
01068
01069
01070 else if (chkid == "FGbz" && (djvu || djvi))
01071 {
01072 if (fgpm || fgbc)
01073 G_THROW( ERR_MSG("DjVuFile.dupl_foregrnd") );
01074 GP<DjVuPalette> fgbc = DjVuPalette::create();
01075 fgbc->decode(gbs);
01076 this->fgbc = fgbc;
01077 desc.format( ERR_MSG("DjVuFile.JB2_fg") "\t%d\t%d",
01078 fgbc->size(), fgbc->colordata.size());
01079 }
01080
01081
01082 else if ((chkid == "PM44" || chkid=="BM44") && iw44)
01083 {
01084 if (!bg44)
01085 {
01086
01087 GP<IW44Image> bg44 = IW44Image::create_decode(IW44Image::COLOR);
01088 bg44->decode_chunk(gbs);
01089 GP<DjVuInfo> info = DjVuInfo::create();
01090 info->width = bg44->get_width();
01091 info->height = bg44->get_height();
01092 info->dpi = 100;
01093 this->bg44 = bg44;
01094 this->info = info;
01095 desc.format( ERR_MSG("DjVuFile.IW44_data1") "\t%d\t%d\t%d",
01096 bg44->get_width(), bg44->get_height(),
01097 get_dpi(bg44->get_width(), bg44->get_height()));
01098 }
01099 else
01100 {
01101
01102 GP<IW44Image> bg44 = this->bg44;
01103 bg44->decode_chunk(gbs);
01104 desc.format( ERR_MSG("DjVuFile.IW44_data2") "\t%d\t%d",
01105 bg44->get_serial(),
01106 get_dpi(bg44->get_width(), bg44->get_height()));
01107 }
01108 }
01109
01110
01111 else if (chkid == "NDIR")
01112 {
01113 GP<DjVuNavDir> dir=DjVuNavDir::create(url);
01114 dir->decode(bs);
01115 this->dir=dir;
01116 desc.format( ERR_MSG("DjVuFile.nav_dir") );
01117 }
01118
01119
01120 else if (chkid == "FORM:ANNO")
01121 {
01122 const GP<ByteStream> gachunk(ByteStream::create());
01123 ByteStream &achunk=*gachunk;
01124 achunk.copy(bs);
01125 achunk.seek(0);
01126 GCriticalSectionLock lock(&anno_lock);
01127 if (! anno)
01128 {
01129 anno=ByteStream::create();
01130 }
01131 anno->seek(0,SEEK_END);
01132 if (anno->tell())
01133 {
01134 anno->write((void*)"", 1);
01135 }
01136
01137 anno->copy(achunk);
01138 desc.format( ERR_MSG("DjVuFile.anno1") );
01139 }
01140
01141
01142 else if (is_annotation(chkid))
01143 {
01144 const GP<ByteStream> gachunk(ByteStream::create());
01145 ByteStream &achunk=*gachunk;
01146 achunk.copy(bs);
01147 achunk.seek(0);
01148 GCriticalSectionLock lock(&anno_lock);
01149 if (! anno)
01150 {
01151 anno = ByteStream::create();
01152 }
01153 anno->seek(0,SEEK_END);
01154 if (anno->tell() & 1)
01155 {
01156 anno->write((const void*)"", 1);
01157 }
01158
01159 const GP<IFFByteStream> giffout(IFFByteStream::create(anno));
01160 IFFByteStream &iffout=*giffout;
01161 iffout.put_chunk(id);
01162 iffout.copy(achunk);
01163 iffout.close_chunk();
01164 desc.format( ERR_MSG("DjVuFile.anno2") );
01165 }
01166 else if (is_text(chkid))
01167 {
01168 const GP<ByteStream> gachunk(ByteStream::create());
01169 ByteStream &achunk=*gachunk;
01170 achunk.copy(bs);
01171 achunk.seek(0);
01172 GCriticalSectionLock lock(&text_lock);
01173 if (! text)
01174 {
01175 text = ByteStream::create();
01176 }
01177 text->seek(0,SEEK_END);
01178 if (text->tell())
01179 {
01180 text->write((const void*)"", 1);
01181 }
01182
01183 const GP<IFFByteStream> giffout(IFFByteStream::create(text));
01184 IFFByteStream &iffout=*giffout;
01185 iffout.put_chunk(id);
01186 iffout.copy(achunk);
01187 iffout.close_chunk();
01188 desc.format( ERR_MSG("DjVuFile.text") );
01189 }
01190 else if (is_meta(chkid))
01191 {
01192 const GP<ByteStream> gachunk(ByteStream::create());
01193 ByteStream &achunk=*gachunk;
01194 achunk.copy(bs);
01195 achunk.seek(0);
01196 GCriticalSectionLock lock(&text_lock);
01197 if (! meta)
01198 {
01199 meta = ByteStream::create();
01200 }
01201 meta->seek(0,SEEK_END);
01202 if (meta->tell())
01203 {
01204 meta->write((const void*)"", 1);
01205 }
01206
01207 const GP<IFFByteStream> giffout(IFFByteStream::create(meta));
01208 IFFByteStream &iffout=*giffout;
01209 iffout.put_chunk(id);
01210 iffout.copy(achunk);
01211 iffout.close_chunk();
01212
01213 }
01214
01215
01216 return desc;
01217 }
01218
01219 void
01220 DjVuFile::set_decode_codec(GP<GPixmap> (*codec)(ByteStream &bs))
01221 {
01222 djvu_decode_codec=codec;
01223 }
01224
01225 void
01226 DjVuFile::decode(const GP<ByteStream> &gbs)
01227 {
01228 check();
01229 DEBUG_MSG("DjVuFile::decode(), url='" << url << "'\n");
01230 DEBUG_MAKE_INDENT(3);
01231 DjVuPortcaster * pcaster=get_portcaster();
01232
01233
01234 GUTF8String chkid;
01235 const GP<IFFByteStream> giff(IFFByteStream::create(gbs));
01236 IFFByteStream &iff=*giff;
01237 if (!iff.get_chunk(chkid))
01238 REPORT_EOF(true)
01239
01240
01241 bool djvi = (chkid=="FORM:DJVI")?true:false;
01242 bool djvu = (chkid=="FORM:DJVU")?true:false;
01243 bool iw44 = ((chkid=="FORM:PM44") || (chkid=="FORM:BM44"));
01244 if (djvi || djvu)
01245 mimetype = "image/x.djvu";
01246 else if (iw44)
01247 mimetype = "image/x-iw44";
01248 else
01249 G_THROW( ERR_MSG("DjVuFile.unexp_image") );
01250
01251
01252 int size_so_far=iff.tell();
01253 int chunks=0;
01254 int last_chunk=0;
01255 G_TRY
01256 {
01257 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
01258 int chksize;
01259 for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
01260 {
01261 chunks++;
01262
01263
01264 GUTF8String str = decode_chunk(chkid, iff.get_bytestream(), djvi, djvu, iw44);
01265
01266 GUTF8String desc;
01267 desc.format("\t%5.1f\t%s", chksize/1024.0, (const char*)chkid);
01268
01269 description = description + str + desc + "\n";
01270
01271 pcaster->notify_chunk_done(this, chkid);
01272
01273 iff.seek_close_chunk();
01274
01275 size_so_far=iff.tell();
01276 }
01277 if (chunks_number < 0) chunks_number=last_chunk;
01278 }
01279 G_CATCH(ex)
01280 {
01281 if(!ex.cmp_cause(ByteStream::EndOfFile))
01282 {
01283 if (chunks_number < 0)
01284 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
01285 report_error(ex,(recover_errors <= SKIP_PAGES));
01286 }else
01287 {
01288 report_error(ex,true);
01289 }
01290 }
01291 G_ENDCATCH;
01292
01293
01294 file_size=size_so_far;
01295
01296 iff.close_chunk();
01297
01298 if (bg44)
01299 bg44->close_codec();
01300
01301
01302 if (djvu && !info)
01303 G_THROW( ERR_MSG("DjVuFile.corrupt_missing_info") );
01304 if (iw44 && !info)
01305 G_THROW( ERR_MSG("DjVuFile.corrupt_missing_IW44") );
01306 if (info)
01307 {
01308 GUTF8String desc;
01309 if (djvu || djvi)
01310 desc.format( ERR_MSG("DjVuFile.djvu_header") "\t%d\t%d\t%d\t%d",
01311 info->width, info->height,
01312 info->dpi, info->version);
01313 else if (iw44)
01314 desc.format( ERR_MSG("DjVuFile.IW44_header") "\t%d\t%d\t%d",
01315 info->width, info->height, info->dpi);
01316 description=desc + "\n" + description;
01317 int rawsize=info->width*info->height*3;
01318 desc.format( ERR_MSG("DjVuFile.ratio") "\t%0.1f\t%0.1f",
01319 (double)rawsize/file_size, file_size/1024.0 );
01320 description=description+desc;
01321 }
01322 }
01323
01324 void
01325 DjVuFile::start_decode(void)
01326 {
01327 check();
01328 DEBUG_MSG("DjVuFile::start_decode(), url='" << url << "'\n");
01329 DEBUG_MAKE_INDENT(3);
01330
01331 GThread * thread_to_delete=0;
01332 flags.enter();
01333 G_TRY {
01334 if (!(flags & DONT_START_DECODE) && !is_decoding())
01335 {
01336 if (flags & DECODE_STOPPED) reset();
01337 flags&=~(DECODE_OK | DECODE_STOPPED | DECODE_FAILED);
01338 flags|=DECODING;
01339
01340
01341
01342 thread_to_delete=decode_thread; decode_thread=0;
01343
01344
01345
01346 decode_data_pool=DataPool::create(data_pool);
01347 decode_life_saver=this;
01348
01349 decode_thread=new GThread();
01350 decode_thread->create(static_decode_func, this);
01351 }
01352 }
01353 G_CATCH_ALL
01354 {
01355 flags&=~DECODING;
01356 flags|=DECODE_FAILED;
01357 flags.leave();
01358 get_portcaster()->notify_file_flags_changed(this, DECODE_FAILED, DECODING);
01359 delete thread_to_delete;
01360 G_RETHROW;
01361 }
01362 G_ENDCATCH;
01363 flags.leave();
01364 delete thread_to_delete;
01365 }
01366
01367 bool
01368 DjVuFile::resume_decode(const bool sync)
01369 {
01370 bool retval=false;
01371 {
01372 GMonitorLock lock(&flags);
01373 if( !is_decoding() && !is_decode_ok() && !is_decode_failed() )
01374 {
01375 start_decode();
01376 retval=true;
01377 }
01378 }
01379 if(sync)
01380 {
01381 wait_for_finish();
01382 }
01383 return retval;
01384 }
01385
01386 void
01387 DjVuFile::stop_decode(bool sync)
01388 {
01389 check();
01390
01391 DEBUG_MSG("DjVuFile::stop_decode(), url='" << url <<
01392 "', sync=" << (int) sync << "\n");
01393 DEBUG_MAKE_INDENT(3);
01394
01395 G_TRY
01396 {
01397 flags|=DONT_START_DECODE;
01398
01399
01400 {
01401
01402 GCriticalSectionLock lock(&inc_files_lock);
01403 for(GPosition pos=inc_files_list;pos;++pos)
01404 inc_files_list[pos]->stop_decode(0);
01405
01406
01407 }
01408
01409 if (sync)
01410 {
01411 while(1)
01412 {
01413 GP<DjVuFile> file;
01414 {
01415 GCriticalSectionLock lock(&inc_files_lock);
01416 for(GPosition pos=inc_files_list;pos;++pos)
01417 {
01418 GP<DjVuFile> & f=inc_files_list[pos];
01419 if (f->is_decoding())
01420 {
01421 file=f; break;
01422 }
01423 }
01424 }
01425 if (!file) break;
01426
01427 file->stop_decode(1);
01428 }
01429
01430 wait_for_finish(1);
01431
01432
01433
01434
01435 }
01436 flags&=~(DONT_START_DECODE);
01437 } G_CATCH_ALL {
01438 flags&=~(DONT_START_DECODE);
01439 G_RETHROW;
01440 } G_ENDCATCH;
01441 }
01442
01443 void
01444 DjVuFile::stop(bool only_blocked)
01445
01446
01447 {
01448 DEBUG_MSG("DjVuFile::stop(): Stopping everything\n");
01449 DEBUG_MAKE_INDENT(3);
01450
01451 flags|=only_blocked ? BLOCKED_STOPPED : STOPPED;
01452 if (data_pool) data_pool->stop(only_blocked);
01453 GCriticalSectionLock lock(&inc_files_lock);
01454 for(GPosition pos=inc_files_list;pos;++pos)
01455 inc_files_list[pos]->stop(only_blocked);
01456 }
01457
01458 GP<DjVuNavDir>
01459 DjVuFile::find_ndir(GMap<GURL, void *> & map)
01460 {
01461 check();
01462
01463 DEBUG_MSG("DjVuFile::find_ndir(): looking for NDIR in '" << url << "'\n");
01464 DEBUG_MAKE_INDENT(3);
01465
01466 if (dir) return dir;
01467
01468 if (!map.contains(url))
01469 {
01470 map[url]=0;
01471
01472 GPList<DjVuFile> list=get_included_files(false);
01473 for(GPosition pos=list;pos;++pos)
01474 {
01475 GP<DjVuNavDir> d=list[pos]->find_ndir(map);
01476 if (d) return d;
01477 }
01478 }
01479 return 0;
01480 }
01481
01482 GP<DjVuNavDir>
01483 DjVuFile::find_ndir(void)
01484 {
01485 GMap<GURL, void *> map;
01486 return find_ndir(map);
01487 }
01488
01489 GP<DjVuNavDir>
01490 DjVuFile::decode_ndir(GMap<GURL, void *> & map)
01491 {
01492 check();
01493
01494 DEBUG_MSG("DjVuFile::decode_ndir(): decoding for NDIR in '" << url << "'\n");
01495 DEBUG_MAKE_INDENT(3);
01496
01497 if (dir) return dir;
01498
01499 if (!map.contains(url))
01500 {
01501 map[url]=0;
01502
01503 const GP<ByteStream> str(data_pool->get_stream());
01504
01505 GUTF8String chkid;
01506 const GP<IFFByteStream> giff(IFFByteStream::create(str));
01507 IFFByteStream &iff=*giff;
01508 if (!iff.get_chunk(chkid))
01509 REPORT_EOF(true)
01510
01511 int chunks=0;
01512 int last_chunk=0;
01513 G_TRY
01514 {
01515 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
01516 int chksize;
01517 for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
01518 {
01519 chunks++;
01520 if (chkid=="NDIR")
01521 {
01522 GP<DjVuNavDir> d=DjVuNavDir::create(url);
01523 d->decode(*iff.get_bytestream());
01524 dir=d;
01525 break;
01526 }
01527 iff.seek_close_chunk();
01528 }
01529 if ((!dir)&&(chunks_number < 0)) chunks_number=last_chunk;
01530 }
01531 G_CATCH(ex)
01532 {
01533 if(!ex.cmp_cause(ByteStream::EndOfFile))
01534 {
01535 if (chunks_number < 0)
01536 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
01537 report_error(ex,(recover_errors<=SKIP_PAGES));
01538 }else
01539 {
01540 report_error(ex,true);
01541 }
01542 }
01543 G_ENDCATCH;
01544
01545 data_pool->clear_stream();
01546 if (dir) return dir;
01547
01548 GPList<DjVuFile> list=get_included_files(false);
01549 for(GPosition pos=list;pos;++pos)
01550 {
01551 GP<DjVuNavDir> d=list[pos]->decode_ndir(map);
01552 if (d) return d;
01553 }
01554 data_pool->clear_stream();
01555 }
01556 return 0;
01557 }
01558
01559 GP<DjVuNavDir>
01560 DjVuFile::decode_ndir(void)
01561 {
01562 GMap<GURL, void *> map;
01563 return decode_ndir(map);
01564 }
01565
01566 void
01567 DjVuFile::get_merged_anno(const GP<DjVuFile> & file,
01568 const GP<ByteStream> &gstr_out, const GList<GURL> & ignore_list,
01569 int level, int & max_level, GMap<GURL, void *> & map)
01570 {
01571 DEBUG_MSG("DjVuFile::get_merged_anno()\n");
01572 GURL url=file->get_url();
01573 if (!map.contains(url))
01574 {
01575 ByteStream &str_out=*gstr_out;
01576 map[url]=0;
01577
01578
01579
01580
01581
01582
01583 GPList<DjVuFile> list=file->get_included_files(!file->is_data_present());
01584 for(GPosition pos=list;pos;++pos)
01585 get_merged_anno(list[pos], gstr_out, ignore_list, level+1, max_level, map);
01586
01587
01588 if (!ignore_list.contains(file->get_url()))
01589 {
01590 if (!file->is_data_present() ||
01591 file->is_modified() && file->anno)
01592 {
01593
01594 GCriticalSectionLock lock(&file->anno_lock);
01595 if (file->anno && file->anno->size())
01596 {
01597 if (str_out.tell())
01598 {
01599 str_out.write((void *) "", 1);
01600 }
01601 file->anno->seek(0);
01602 str_out.copy(*file->anno);
01603 }
01604 } else if (file->is_data_present())
01605 {
01606
01607
01608 const GP<ByteStream> str(file->data_pool->get_stream());
01609 const GP<IFFByteStream> giff(IFFByteStream::create(str));
01610 IFFByteStream &iff=*giff;
01611 GUTF8String chkid;
01612 if (iff.get_chunk(chkid))
01613 while(iff.get_chunk(chkid))
01614 {
01615 if (chkid=="FORM:ANNO")
01616 {
01617 if (max_level<level)
01618 max_level=level;
01619 if (str_out.tell())
01620 {
01621 str_out.write((void *) "", 1);
01622 }
01623 str_out.copy(*iff.get_bytestream());
01624 }
01625 else if (is_annotation(chkid))
01626 {
01627 if (max_level<level)
01628 max_level=level;
01629 if (str_out.tell()&&chkid != "ANTz")
01630 {
01631 str_out.write((void *) "", 1);
01632 }
01633 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01634 IFFByteStream &iff_out=*giff_out;
01635 iff_out.put_chunk(chkid);
01636 iff_out.copy(*iff.get_bytestream());
01637 iff_out.close_chunk();
01638 }
01639 iff.close_chunk();
01640 }
01641 file->data_pool->clear_stream();
01642 }
01643 }
01644 }
01645 }
01646
01647 GP<ByteStream>
01648 DjVuFile::get_merged_anno(const GList<GURL> & ignore_list,
01649 int * max_level_ptr)
01650
01651
01652 {
01653 DEBUG_MSG("DjVuFile::get_merged_anno()\n");
01654 GP<ByteStream> gstr(ByteStream::create());
01655 GMap<GURL, void *> map;
01656 int max_level=0;
01657 get_merged_anno(this, gstr, ignore_list, 0, max_level, map);
01658 if (max_level_ptr)
01659 *max_level_ptr=max_level;
01660 ByteStream &str=*gstr;
01661 if (!str.tell())
01662 {
01663 gstr=0;
01664 }else
01665 {
01666 str.seek(0);
01667 }
01668 return gstr;
01669 }
01670
01671 GP<ByteStream>
01672 DjVuFile::get_merged_anno(int * max_level_ptr)
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684 {
01685 GList<GURL> ignore_list;
01686 return get_merged_anno(ignore_list, max_level_ptr);
01687 }
01688
01689
01690
01691
01692
01693 void
01694 DjVuFile::get_anno(
01695 const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01696 {
01697 DEBUG_MSG("DjVuFile::get_anno()\n");
01698 ByteStream &str_out=*gstr_out;
01699 if (!file->is_data_present() ||
01700 file->is_modified() && file->anno)
01701 {
01702
01703 GCriticalSectionLock lock(&file->anno_lock);
01704 if (file->anno && file->anno->size())
01705 {
01706 if (str_out.tell())
01707 {
01708 str_out.write((void *) "", 1);
01709 }
01710 file->anno->seek(0);
01711 str_out.copy(*file->anno);
01712 }
01713 } else if (file->is_data_present())
01714 {
01715
01716
01717 const GP<ByteStream> str=file->data_pool->get_stream();
01718 const GP<IFFByteStream> giff=IFFByteStream::create(str);
01719 IFFByteStream &iff=*giff;
01720 GUTF8String chkid;
01721 if (iff.get_chunk(chkid))
01722 {
01723 while(iff.get_chunk(chkid))
01724 {
01725 if (is_annotation(chkid))
01726 {
01727 if (str_out.tell())
01728 {
01729 str_out.write((void *) "", 1);
01730 }
01731 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01732 IFFByteStream &iff_out=*giff_out;
01733 iff_out.put_chunk(chkid);
01734 iff_out.copy(*iff.get_bytestream());
01735 iff_out.close_chunk();
01736 }
01737 iff.close_chunk();
01738 }
01739 }
01740 file->data_pool->clear_stream();
01741 }
01742 }
01743
01744 void
01745 DjVuFile::get_text(
01746 const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01747 {
01748 DEBUG_MSG("DjVuFile::get_text()\n");
01749 ByteStream &str_out=*gstr_out;
01750 if (!file->is_data_present() ||
01751 file->is_modified() && file->text)
01752 {
01753
01754 GCriticalSectionLock lock(&file->text_lock);
01755 if (file->text && file->text->size())
01756 {
01757 if (str_out.tell())
01758 {
01759 str_out.write((void *) "", 1);
01760 }
01761 file->text->seek(0);
01762 str_out.copy(*file->text);
01763 }
01764 } else if (file->is_data_present())
01765 {
01766
01767
01768 const GP<ByteStream> str=file->data_pool->get_stream();
01769 const GP<IFFByteStream> giff=IFFByteStream::create(str);
01770 IFFByteStream &iff=*giff;
01771 GUTF8String chkid;
01772 if (iff.get_chunk(chkid))
01773 {
01774 while(iff.get_chunk(chkid))
01775 {
01776 if (is_text(chkid))
01777 {
01778 if (str_out.tell())
01779 {
01780 str_out.write((void *) "", 1);
01781 }
01782 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01783 IFFByteStream &iff_out=*giff_out;
01784 iff_out.put_chunk(chkid);
01785 iff_out.copy(*iff.get_bytestream());
01786 iff_out.close_chunk();
01787 }
01788 iff.close_chunk();
01789 }
01790 }
01791 file->data_pool->clear_stream();
01792 }
01793 }
01794
01795 void
01796 DjVuFile::get_meta(
01797 const GP<DjVuFile> & file, const GP<ByteStream> &gstr_out)
01798 {
01799 DEBUG_MSG("DjVuFile::get_meta()\n");
01800 ByteStream &str_out=*gstr_out;
01801 if (!file->is_data_present() ||
01802 file->is_modified() && file->meta)
01803 {
01804
01805 GCriticalSectionLock lock(&file->meta_lock);
01806 if (file->meta && file->meta->size())
01807 {
01808 if (str_out.tell())
01809 {
01810 str_out.write((void *) "", 1);
01811 }
01812 file->meta->seek(0);
01813 str_out.copy(*file->meta);
01814 }
01815 } else if (file->is_data_present())
01816 {
01817
01818
01819 const GP<ByteStream> str=file->data_pool->get_stream();
01820 const GP<IFFByteStream> giff=IFFByteStream::create(str);
01821 IFFByteStream &iff=*giff;
01822 GUTF8String chkid;
01823 if (iff.get_chunk(chkid))
01824 {
01825 while(iff.get_chunk(chkid))
01826 {
01827 if (is_meta(chkid))
01828 {
01829 if (str_out.tell())
01830 {
01831 str_out.write((void *) "", 1);
01832 }
01833 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
01834 IFFByteStream &iff_out=*giff_out;
01835 iff_out.put_chunk(chkid);
01836 iff_out.copy(*iff.get_bytestream());
01837 iff_out.close_chunk();
01838 }
01839 iff.close_chunk();
01840 }
01841 }
01842 file->data_pool->clear_stream();
01843 }
01844 }
01845
01846 GP<ByteStream>
01847 DjVuFile::get_anno(void)
01848 {
01849 DEBUG_MSG("DjVuFile::get_text(void)\n");
01850 GP<ByteStream> gstr(ByteStream::create());
01851 get_anno(this, gstr);
01852 ByteStream &str=*gstr;
01853 if (!str.tell())
01854 {
01855 gstr=0;
01856 }else
01857 {
01858 str.seek(0);
01859 }
01860 return gstr;
01861 }
01862
01863 GP<ByteStream>
01864 DjVuFile::get_text(void)
01865 {
01866 DEBUG_MSG("DjVuFile::get_text(void)\n");
01867 GP<ByteStream> gstr(ByteStream::create());
01868 get_text(this, gstr);
01869 ByteStream &str=*gstr;
01870 if (!str.tell())
01871 {
01872 gstr=0;
01873 }else
01874 {
01875 str.seek(0);
01876 }
01877 return gstr;
01878 }
01879
01880 GP<ByteStream>
01881 DjVuFile::get_meta(void)
01882 {
01883 DEBUG_MSG("DjVuFile::get_meta(void)\n");
01884 GP<ByteStream> gstr(ByteStream::create());
01885 get_meta(this, gstr);
01886 ByteStream &str=*gstr;
01887 if (!str.tell())
01888 {
01889 gstr=0;
01890 }else
01891 {
01892 str.seek(0);
01893 }
01894 return gstr;
01895 }
01896
01897 void
01898 DjVuFile::static_trigger_cb(void * cl_data)
01899 {
01900 DjVuFile * th=(DjVuFile *) cl_data;
01901 G_TRY {
01902 GP<DjVuPort> port=DjVuPort::get_portcaster()->is_port_alive(th);
01903 if (port && port->inherits("DjVuFile"))
01904 ((DjVuFile *) (DjVuPort *) port)->trigger_cb();
01905 } G_CATCH(exc) {
01906 G_TRY {
01907 get_portcaster()->notify_error(th, exc.get_cause());
01908 } G_CATCH_ALL {} G_ENDCATCH;
01909 } G_ENDCATCH;
01910 }
01911
01912 void
01913 DjVuFile::trigger_cb(void)
01914 {
01915 GP<DjVuFile> life_saver=this;
01916
01917 DEBUG_MSG("DjVuFile::trigger_cb(): got data for '" << url << "'\n");
01918 DEBUG_MAKE_INDENT(3);
01919
01920 file_size=data_pool->get_length();
01921 flags|=DATA_PRESENT;
01922 get_portcaster()->notify_file_flags_changed(this, DATA_PRESENT, 0);
01923
01924 if (!are_incl_files_created())
01925 process_incl_chunks();
01926
01927 bool all=true;
01928 inc_files_lock.lock();
01929 GPList<DjVuFile> files_list=inc_files_list;
01930 inc_files_lock.unlock();
01931 for(GPosition pos=files_list;pos&&(all=files_list[pos]->is_all_data_present());++pos)
01932 EMPTY_LOOP;
01933 if (all)
01934 {
01935 DEBUG_MSG("DjVuFile::trigger_cb(): We have ALL data for '" << url << "'\n");
01936 flags|=ALL_DATA_PRESENT;
01937 get_portcaster()->notify_file_flags_changed(this, ALL_DATA_PRESENT, 0);
01938 }
01939 }
01940
01941 void
01942 DjVuFile::progress_cb(int pos, void * cl_data)
01943 {
01944 DEBUG_MSG("DjVuFile::progress_cb() called\n");
01945 DEBUG_MAKE_INDENT(3);
01946
01947 DjVuFile * th=(DjVuFile *) cl_data;
01948
01949 int length=th->decode_data_pool->get_length();
01950 if (length>0)
01951 {
01952 float progress=(float) pos/length;
01953 DEBUG_MSG("progress=" << progress << "\n");
01954 get_portcaster()->notify_decode_progress(th, progress);
01955 } else
01956 {
01957 DEBUG_MSG("DataPool size is still unknown => ignoring\n");
01958 }
01959 }
01960
01961
01962
01963
01964
01965 void
01966 DjVuFile::move(GMap<GURL, void *> & map, const GURL & dir_url)
01967
01968 {
01969 if (!map.contains(url))
01970 {
01971 map[url]=0;
01972
01973 url=GURL::UTF8(url.name(),dir_url);
01974
01975
01976
01977 GCriticalSectionLock lock(&inc_files_lock);
01978 for(GPosition pos=inc_files_list;pos;++pos)
01979 inc_files_list[pos]->move(map, dir_url);
01980 }
01981 }
01982
01983 void
01984 DjVuFile::move(const GURL & dir_url)
01985
01986 {
01987 check();
01988 DEBUG_MSG("DjVuFile::move(): dir_url='" << dir_url << "'\n");
01989 DEBUG_MAKE_INDENT(3);
01990
01991 GMap<GURL, void *> map;
01992 move(map, dir_url);
01993 }
01994
01995 void
01996 DjVuFile::set_name(const GUTF8String &name)
01997 {
01998 DEBUG_MSG("DjVuFile::set_name(): name='" << name << "'\n");
01999 DEBUG_MAKE_INDENT(3);
02000 url=GURL::UTF8(name,url.base());
02001 }
02002
02003
02004
02005
02006
02007 int
02008 DjVuFile::get_chunks_number(void)
02009 {
02010 if(chunks_number < 0)
02011 {
02012 const GP<ByteStream> str(data_pool->get_stream());
02013 GUTF8String chkid;
02014 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02015 IFFByteStream &iff=*giff;
02016 if (!iff.get_chunk(chkid))
02017 REPORT_EOF(true)
02018
02019 int chunks=0;
02020 int last_chunk=0;
02021 G_TRY
02022 {
02023 int chksize;
02024 for(;(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02025 {
02026 chunks++;
02027 iff.seek_close_chunk();
02028 }
02029 chunks_number=last_chunk;
02030 }
02031 G_CATCH(ex)
02032 {
02033 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02034 report_error(ex,(recover_errors<=SKIP_PAGES));
02035 }
02036 G_ENDCATCH;
02037 data_pool->clear_stream();
02038 }
02039 return chunks_number;
02040 }
02041
02042 GUTF8String
02043 DjVuFile::get_chunk_name(int chunk_num)
02044 {
02045 if(chunk_num < 0)
02046 {
02047 G_THROW( ERR_MSG("DjVuFile.illegal_chunk") );
02048 }
02049 if((chunks_number >= 0)&&(chunk_num > chunks_number))
02050 {
02051 G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
02052 }
02053 check();
02054
02055 GUTF8String name;
02056 const GP<ByteStream> str(data_pool->get_stream());
02057 GUTF8String chkid;
02058 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02059 IFFByteStream &iff=*giff;
02060 if (!iff.get_chunk(chkid))
02061 REPORT_EOF(true)
02062
02063 int chunks=0;
02064 int last_chunk=0;
02065 G_TRY
02066 {
02067 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02068 int chksize;
02069 for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02070 {
02071 if (chunks++==chunk_num) { name=chkid; break; }
02072 iff.seek_close_chunk();
02073 }
02074 }
02075 G_CATCH(ex)
02076 {
02077 if (chunks_number < 0)
02078 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02079 report_error(ex,(recover_errors <= SKIP_PAGES));
02080 }
02081 G_ENDCATCH;
02082 if (!name.length())
02083 {
02084 if (chunks_number < 0) chunks_number=chunks;
02085 G_THROW( ERR_MSG("DjVuFile.missing_chunk") );
02086 }
02087 return name;
02088 }
02089
02090 bool
02091 DjVuFile::contains_chunk(const GUTF8String &chunk_name)
02092 {
02093 check();
02094 DEBUG_MSG("DjVuFile::contains_chunk(): url='" << url << "', chunk_name='" <<
02095 chunk_name << "'\n");
02096 DEBUG_MAKE_INDENT(3);
02097
02098 bool contains=0;
02099 const GP<ByteStream> str(data_pool->get_stream());
02100 GUTF8String chkid;
02101 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02102 IFFByteStream &iff=*giff;
02103 if (!iff.get_chunk(chkid))
02104 REPORT_EOF((recover_errors<=SKIP_PAGES))
02105
02106 int chunks=0;
02107 int last_chunk=0;
02108 G_TRY
02109 {
02110 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02111 int chksize;
02112 for(;(chunks_left--)&&(chksize=iff.get_chunk(chkid));last_chunk=chunks)
02113 {
02114 chunks++;
02115 if (chkid==chunk_name) { contains=1; break; }
02116 iff.seek_close_chunk();
02117 }
02118 if (!contains &&(chunks_number < 0)) chunks_number=last_chunk;
02119 }
02120 G_CATCH(ex)
02121 {
02122 if (chunks_number < 0)
02123 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02124 report_error(ex,(recover_errors <= SKIP_PAGES));
02125 }
02126 G_ENDCATCH;
02127 data_pool->clear_stream();
02128 return contains;
02129 }
02130
02131 bool
02132 DjVuFile::contains_anno(void)
02133 {
02134 const GP<ByteStream> str(data_pool->get_stream());
02135
02136 GUTF8String chkid;
02137 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02138 IFFByteStream &iff=*giff;
02139 if (!iff.get_chunk(chkid))
02140 G_THROW( ByteStream::EndOfFile );
02141
02142 while(iff.get_chunk(chkid))
02143 {
02144 if (is_annotation(chkid))
02145 return true;
02146 iff.close_chunk();
02147 }
02148
02149 data_pool->clear_stream();
02150 return false;
02151 }
02152
02153 bool
02154 DjVuFile::contains_text(void)
02155 {
02156 const GP<ByteStream> str(data_pool->get_stream());
02157
02158 GUTF8String chkid;
02159 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02160 IFFByteStream &iff=*giff;
02161 if (!iff.get_chunk(chkid))
02162 G_THROW( ByteStream::EndOfFile );
02163
02164 while(iff.get_chunk(chkid))
02165 {
02166 if (is_text(chkid))
02167 return true;
02168 iff.close_chunk();
02169 }
02170
02171 data_pool->clear_stream();
02172 return false;
02173 }
02174
02175 bool
02176 DjVuFile::contains_meta(void)
02177 {
02178 const GP<ByteStream> str(data_pool->get_stream());
02179
02180 GUTF8String chkid;
02181 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02182 IFFByteStream &iff=*giff;
02183 if (!iff.get_chunk(chkid))
02184 G_THROW( ByteStream::EndOfFile );
02185
02186 while(iff.get_chunk(chkid))
02187 {
02188 if (is_meta(chkid))
02189 return true;
02190 iff.close_chunk();
02191 }
02192
02193 data_pool->clear_stream();
02194 return false;
02195 }
02196
02197
02198
02199
02200
02201 static void
02202 copy_chunks(const GP<ByteStream> &from, IFFByteStream &ostr)
02203 {
02204 from->seek(0);
02205 const GP<IFFByteStream> giff(IFFByteStream::create(from));
02206 IFFByteStream &iff=*giff;
02207 GUTF8String chkid;
02208 int chksize;
02209 while ((chksize=iff.get_chunk(chkid)))
02210 {
02211 ostr.put_chunk(chkid);
02212 int ochksize=ostr.copy(*iff.get_bytestream());
02213 ostr.close_chunk();
02214 iff.seek_close_chunk();
02215 if(ochksize != chksize)
02216 {
02217 G_THROW( ByteStream::EndOfFile );
02218 }
02219 }
02220 }
02221
02222
02223 void
02224 DjVuFile::add_djvu_data(IFFByteStream & ostr, GMap<GURL, void *> & map,
02225 const bool included_too, const bool no_ndir)
02226 {
02227 check();
02228 if (map.contains(url)) return;
02229 bool top_level = !map.size();
02230 map[url]=0;
02231 bool processed_annotation = false;
02232 bool processed_text = false;
02233 bool processed_meta = false;
02234
02235 const GP<ByteStream> str(data_pool->get_stream());
02236 GUTF8String chkid;
02237 const GP<IFFByteStream> giff(IFFByteStream::create(str));
02238 IFFByteStream &iff=*giff;
02239 if (!iff.get_chunk(chkid))
02240 REPORT_EOF(true)
02241
02242
02243 if (top_level)
02244 ostr.put_chunk(chkid);
02245
02246 int chunks=0;
02247 int last_chunk=0;
02248 G_TRY
02249 {
02250 int chunks_left=(recover_errors>SKIP_PAGES)?chunks_number:(-1);
02251 int chksize;
02252 for(;(chunks_left--)&&(chksize = iff.get_chunk(chkid));last_chunk=chunks)
02253 {
02254 chunks++;
02255 if (is_info(chkid) && info)
02256 {
02257 ostr.put_chunk(chkid);
02258 info->encode(*ostr.get_bytestream());
02259 ostr.close_chunk();
02260 }
02261 else if (chkid=="INCL" && included_too)
02262 {
02263 GP<DjVuFile> file = process_incl_chunk(*iff.get_bytestream());
02264 if (file)
02265 {
02266 if(recover_errors!=ABORT)
02267 file->set_recover_errors(recover_errors);
02268 if(verbose_eof)
02269 file->set_verbose_eof(verbose_eof);
02270 file->add_djvu_data(ostr, map, included_too, no_ndir);
02271 }
02272 }
02273 else if (is_annotation(chkid) && anno && anno->size())
02274 {
02275 if (!processed_annotation)
02276 {
02277 processed_annotation = true;
02278 GCriticalSectionLock lock(&anno_lock);
02279 copy_chunks(anno, ostr);
02280 }
02281 }
02282 else if (is_text(chkid) && text && text->size())
02283 {
02284 if (!processed_text)
02285 {
02286 processed_text = true;
02287 GCriticalSectionLock lock(&text_lock);
02288 copy_chunks(text, ostr);
02289 }
02290 }
02291 else if (is_meta(chkid) && meta && meta->size())
02292 {
02293 if (!processed_meta)
02294 {
02295 processed_meta = true;
02296 GCriticalSectionLock lock(&meta_lock);
02297 copy_chunks(meta, ostr);
02298 }
02299 }
02300 else if (chkid!="NDIR"||!(no_ndir || dir))
02301 {
02302 ostr.put_chunk(chkid);
02303 ostr.copy(*iff.get_bytestream());
02304 ostr.close_chunk();
02305 }
02306 iff.seek_close_chunk();
02307 }
02308 if (chunks_number < 0) chunks_number=last_chunk;
02309 }
02310 G_CATCH(ex)
02311 {
02312 if(!ex.cmp_cause(ByteStream::EndOfFile))
02313 {
02314 if (chunks_number < 0)
02315 chunks_number=(recover_errors>SKIP_CHUNKS)?chunks:last_chunk;
02316 report_error(ex,(recover_errors<=SKIP_PAGES));
02317 }else
02318 {
02319 report_error(ex,true);
02320 }
02321 }
02322 G_ENDCATCH;
02323
02324
02325 if (!processed_annotation && anno && anno->size())
02326 {
02327 processed_annotation = true;
02328 GCriticalSectionLock lock(&anno_lock);
02329 copy_chunks(anno, ostr);
02330 }
02331 if (!processed_text && text && text->size())
02332 {
02333 processed_text = true;
02334 GCriticalSectionLock lock(&text_lock);
02335 copy_chunks(text, ostr);
02336 }
02337 if (!processed_meta && meta && meta->size())
02338 {
02339 processed_meta = true;
02340 GCriticalSectionLock lock(&meta_lock);
02341 copy_chunks(meta, ostr);
02342 }
02343
02344 if (top_level)
02345 ostr.close_chunk();
02346
02347 data_pool->clear_stream();
02348 }
02349
02350 GP<ByteStream>
02351 DjVuFile::get_djvu_bytestream(const bool included_too, const bool no_ndir)
02352 {
02353 check();
02354 DEBUG_MSG("DjVuFile::get_djvu_bytestream(): creating DjVu raw file\n");
02355 DEBUG_MAKE_INDENT(3);
02356 const GP<ByteStream> pbs(ByteStream::create());
02357 const GP<IFFByteStream> giff=IFFByteStream::create(pbs);
02358 IFFByteStream &iff=*giff;
02359 GMap<GURL, void *> map;
02360 add_djvu_data(iff, map, included_too, no_ndir);
02361 iff.flush();
02362 pbs->seek(0, SEEK_SET);
02363 return pbs;
02364 }
02365
02366 GP<DataPool>
02367 DjVuFile::get_djvu_data(const bool included_too, const bool no_ndir)
02368 {
02369 const GP<ByteStream> pbs = get_djvu_bytestream(included_too, no_ndir);
02370 return DataPool::create(pbs);
02371 }
02372
02373 void
02374 DjVuFile::merge_anno(ByteStream &out)
02375 {
02376
02377
02378
02379
02380
02381
02382
02383 const GP<ByteStream> str(get_merged_anno());
02384 if (str)
02385 {
02386 str->seek(0);
02387 if (out.tell())
02388 {
02389 out.write((void *) "", 1);
02390 }
02391 out.copy(*str);
02392 }
02393 }
02394
02395 void
02396 DjVuFile::get_text(ByteStream &out)
02397 {
02398 const GP<ByteStream> str(get_text());
02399 if (str)
02400 {
02401 str->seek(0);
02402 if (out.tell())
02403 {
02404 out.write((void *) "", 1);
02405 }
02406 out.copy(*str);
02407 }
02408 }
02409
02410 void
02411 DjVuFile::get_meta(ByteStream &out)
02412 {
02413 const GP<ByteStream> str(get_meta());
02414 if (str)
02415 {
02416 str->seek(0);
02417 if (out.tell())
02418 {
02419 out.write((void *) "", 1);
02420 }
02421 out.copy(*str);
02422 }
02423 }
02424
02425
02426
02427
02428
02429
02430
02431 void
02432 DjVuFile::remove_anno(void)
02433 {
02434 DEBUG_MSG("DjVuFile::remove_anno()\n");
02435 const GP<ByteStream> str_in(data_pool->get_stream());
02436 const GP<ByteStream> gstr_out(ByteStream::create());
02437
02438 GUTF8String chkid;
02439 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02440 IFFByteStream &iff_in=*giff_in;
02441 if (!iff_in.get_chunk(chkid))
02442 G_THROW( ByteStream::EndOfFile );
02443
02444 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02445 IFFByteStream &iff_out=*giff_out;
02446 iff_out.put_chunk(chkid);
02447
02448 while(iff_in.get_chunk(chkid))
02449 {
02450 if (!is_annotation(chkid))
02451 {
02452 iff_out.put_chunk(chkid);
02453 iff_out.copy(*iff_in.get_bytestream());
02454 iff_out.close_chunk();
02455 }
02456 iff_in.close_chunk();
02457 }
02458
02459 iff_out.close_chunk();
02460
02461 gstr_out->seek(0, SEEK_SET);
02462 data_pool=DataPool::create(gstr_out);
02463 chunks_number=-1;
02464
02465 anno=0;
02466
02467 flags|=MODIFIED;
02468 data_pool->clear_stream();
02469 }
02470
02471 void
02472 DjVuFile::remove_text(void)
02473 {
02474 DEBUG_MSG("DjVuFile::remove_text()\n");
02475 const GP<ByteStream> str_in(data_pool->get_stream());
02476 const GP<ByteStream> gstr_out(ByteStream::create());
02477
02478 GUTF8String chkid;
02479 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02480 IFFByteStream &iff_in=*giff_in;
02481 if (!iff_in.get_chunk(chkid))
02482 G_THROW( ByteStream::EndOfFile );
02483
02484 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02485 IFFByteStream &iff_out=*giff_out;
02486 iff_out.put_chunk(chkid);
02487
02488 while(iff_in.get_chunk(chkid))
02489 {
02490 if (!is_text(chkid))
02491 {
02492 iff_out.put_chunk(chkid);
02493 iff_out.copy(*iff_in.get_bytestream());
02494 iff_out.close_chunk();
02495 }
02496 iff_in.close_chunk();
02497 }
02498
02499 iff_out.close_chunk();
02500
02501 gstr_out->seek(0, SEEK_SET);
02502 data_pool=DataPool::create(gstr_out);
02503 chunks_number=-1;
02504
02505 text=0;
02506
02507 flags|=MODIFIED;
02508 data_pool->clear_stream();
02509 }
02510
02511 void
02512 DjVuFile::remove_meta(void)
02513 {
02514 DEBUG_MSG("DjVuFile::remove_meta()\n");
02515 const GP<ByteStream> str_in(data_pool->get_stream());
02516 const GP<ByteStream> gstr_out(ByteStream::create());
02517
02518 GUTF8String chkid;
02519 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02520 IFFByteStream &iff_in=*giff_in;
02521 if (!iff_in.get_chunk(chkid))
02522 G_THROW( ByteStream::EndOfFile );
02523
02524 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02525 IFFByteStream &iff_out=*giff_out;
02526 iff_out.put_chunk(chkid);
02527
02528 while(iff_in.get_chunk(chkid))
02529 {
02530 if (!is_meta(chkid))
02531 {
02532 iff_out.put_chunk(chkid);
02533 iff_out.copy(*iff_in.get_bytestream());
02534 iff_out.close_chunk();
02535 }
02536 iff_in.close_chunk();
02537 }
02538
02539 iff_out.close_chunk();
02540
02541 gstr_out->seek(0, SEEK_SET);
02542 data_pool=DataPool::create(gstr_out);
02543 chunks_number=-1;
02544
02545 meta=0;
02546
02547 flags|=MODIFIED;
02548 data_pool->clear_stream();
02549 }
02550
02551 void
02552 DjVuFile::rebuild_data_pool(void)
02553 {
02554 data_pool=get_djvu_data(false,false);
02555 chunks_number=1;
02556 flags|=MODIFIED;
02557 }
02558
02559
02560
02561
02562 GP<DataPool>
02563 DjVuFile::unlink_file(const GP<DataPool> & data, const GUTF8String &name)
02564
02565
02566 {
02567 DEBUG_MSG("DjVuFile::unlink_file()\n");
02568 const GP<ByteStream> gstr_out(ByteStream::create());
02569 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02570 IFFByteStream &iff_out=*giff_out;
02571
02572 const GP<ByteStream> str_in(data->get_stream());
02573 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02574 IFFByteStream &iff_in=*giff_in;
02575
02576 int chksize;
02577 GUTF8String chkid;
02578 if (!iff_in.get_chunk(chkid)) return data;
02579
02580 iff_out.put_chunk(chkid);
02581
02582 while((chksize=iff_in.get_chunk(chkid)))
02583 {
02584 if (chkid=="INCL")
02585 {
02586 GUTF8String incl_str;
02587 char buffer[1024];
02588 int length;
02589 while((length=iff_in.read(buffer, 1024)))
02590 incl_str+=GUTF8String(buffer, length);
02591
02592
02593 while(incl_str.length() && incl_str[0]=='\n')
02594 {
02595 incl_str=incl_str.substr(1,(unsigned int)(-1));
02596 }
02597 while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
02598 {
02599 incl_str.setat(incl_str.length()-1, 0);
02600 }
02601 if (incl_str!=name)
02602 {
02603 iff_out.put_chunk(chkid);
02604 iff_out.get_bytestream()->writestring(incl_str);
02605 iff_out.close_chunk();
02606 }
02607 } else
02608 {
02609 iff_out.put_chunk(chkid);
02610 char buffer[1024];
02611 int length;
02612 for(const GP<ByteStream> gbs(iff_out.get_bytestream());
02613 (length=iff_in.read(buffer, 1024));)
02614 {
02615 gbs->writall(buffer, length);
02616 }
02617 iff_out.close_chunk();
02618 }
02619 iff_in.close_chunk();
02620 }
02621 iff_out.close_chunk();
02622 iff_out.flush();
02623 gstr_out->seek(0, SEEK_SET);
02624 data->clear_stream();
02625 return DataPool::create(gstr_out);
02626 }
02627
02628 #ifndef NEED_DECODER_ONLY
02629 void
02630 DjVuFile::insert_file(const GUTF8String &id, int chunk_num)
02631 {
02632 DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "', chunk_num="
02633 << chunk_num << "\n");
02634 DEBUG_MAKE_INDENT(3);
02635
02636
02637 const GP<ByteStream> str_in(data_pool->get_stream());
02638 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02639 IFFByteStream &iff_in=*giff_in;
02640
02641 const GP<ByteStream> gstr_out(ByteStream::create());
02642 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02643 IFFByteStream &iff_out=*giff_out;
02644
02645 int chunk_cnt=0;
02646 bool done=false;
02647 GUTF8String chkid;
02648 if (iff_in.get_chunk(chkid))
02649 {
02650 iff_out.put_chunk(chkid);
02651 int chksize;
02652 while((chksize=iff_in.get_chunk(chkid)))
02653 {
02654 if (chunk_cnt++==chunk_num)
02655 {
02656 iff_out.put_chunk("INCL");
02657 iff_out.get_bytestream()->writestring(id);
02658 iff_out.close_chunk();
02659 done=true;
02660 }
02661 iff_out.put_chunk(chkid);
02662 iff_out.copy(*iff_in.get_bytestream());
02663 iff_out.close_chunk();
02664 iff_in.close_chunk();
02665 }
02666 if (!done)
02667 {
02668 iff_out.put_chunk("INCL");
02669 iff_out.get_bytestream()->writestring(id);
02670 iff_out.close_chunk();
02671 }
02672 iff_out.close_chunk();
02673 }
02674 gstr_out->seek(0, SEEK_SET);
02675 data_pool=DataPool::create(gstr_out);
02676 chunks_number=-1;
02677
02678
02679 process_incl_chunks();
02680
02681 flags|=MODIFIED;
02682 data_pool->clear_stream();
02683 }
02684 #endif
02685
02686 void
02687 DjVuFile::unlink_file(const GUTF8String &id)
02688 {
02689 DEBUG_MSG("DjVuFile::insert_file(): id='" << id << "'\n");
02690 DEBUG_MAKE_INDENT(3);
02691
02692
02693 {
02694 GURL url=DjVuPort::get_portcaster()->id_to_url(this, id);
02695 if (url.is_empty()) url=GURL::UTF8(id,this->url.base());
02696 GCriticalSectionLock lock(&inc_files_lock);
02697 for(GPosition pos=inc_files_list;pos;)
02698 if (inc_files_list[pos]->get_url()==url)
02699 {
02700 GPosition this_pos=pos;
02701 ++pos;
02702 inc_files_list.del(this_pos);
02703 } else ++pos;
02704 }
02705
02706
02707 const GP<ByteStream> str_in(data_pool->get_stream());
02708 const GP<IFFByteStream> giff_in(IFFByteStream::create(str_in));
02709 IFFByteStream &iff_in=*giff_in;
02710
02711 const GP<ByteStream> gstr_out(ByteStream::create());
02712 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
02713 IFFByteStream &iff_out=*giff_out;
02714
02715 GUTF8String chkid;
02716 if (iff_in.get_chunk(chkid))
02717 {
02718 iff_out.put_chunk(chkid);
02719 int chksize;
02720 while((chksize=iff_in.get_chunk(chkid)))
02721 {
02722 if (chkid!="INCL")
02723 {
02724 iff_out.put_chunk(chkid);
02725 iff_out.copy(*iff_in.get_bytestream());
02726 iff_out.close_chunk();
02727 } else
02728 {
02729 GUTF8String incl_str;
02730 char buffer[1024];
02731 int length;
02732 while((length=iff_in.read(buffer, 1024)))
02733 incl_str+=GUTF8String(buffer, length);
02734
02735
02736 while(incl_str.length() && incl_str[0]=='\n')
02737 {
02738 incl_str=incl_str.substr(1,(unsigned int)(-1));
02739 }
02740 while(incl_str.length()>0 && incl_str[(int)incl_str.length()-1]=='\n')
02741 incl_str.setat(incl_str.length()-1, 0);
02742 if (incl_str!=id)
02743 {
02744 iff_out.put_chunk("INCL");
02745 iff_out.get_bytestream()->writestring(incl_str);
02746 iff_out.close_chunk();
02747 }
02748 }
02749 iff_in.close_chunk();
02750 }
02751 iff_out.close_chunk();
02752 }
02753
02754 gstr_out->seek(0, SEEK_SET);
02755 data_pool=DataPool::create(gstr_out);
02756 chunks_number=-1;
02757
02758 flags|=MODIFIED;
02759 }
02760
02761 void
02762 DjVuFile::change_info(GP<DjVuInfo> xinfo,const bool do_reset)
02763 {
02764 DEBUG_MSG("DjVuFile::change_text()\n");
02765
02766 set_modified(true);
02767 if(do_reset)
02768 reset();
02769 info=xinfo;
02770 }
02771
02772 #ifndef NEED_DECODER_ONLY
02773 void
02774 DjVuFile::change_text(GP<DjVuTXT> txt,const bool do_reset)
02775 {
02776 DEBUG_MSG("DjVuFile::change_text()\n");
02777 GP<DjVuText> gtext_c=DjVuText::create();
02778 DjVuText &text_c=*gtext_c;
02779 if(contains_text())
02780 {
02781 const GP<ByteStream> file_text(get_text());
02782 if(file_text)
02783 {
02784 text_c.decode(file_text);
02785 }
02786 }
02787 GCriticalSectionLock lock(&text_lock);
02788
02789 set_modified(true);
02790 if(do_reset)
02791 reset();
02792 text_c.txt = txt;
02793 text=ByteStream::create();
02794 text_c.encode(text);
02795 }
02796
02797 void
02798 DjVuFile::change_meta(const GUTF8String &xmeta,const bool do_reset)
02799 {
02800 DEBUG_MSG("DjVuFile::change_meta()\n");
02801
02802 set_modified(true);
02803 if(contains_meta())
02804 {
02805 (void)get_meta();
02806 }
02807 if(do_reset)
02808 reset();
02809 GCriticalSectionLock lock(&meta_lock);
02810 meta=ByteStream::create();
02811 if(xmeta.length())
02812 {
02813 const GP<IFFByteStream> giff=IFFByteStream::create(meta);
02814 IFFByteStream &iff=*giff;
02815 iff.put_chunk("METz");
02816 {
02817 GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50);
02818 gbsiff->writestring(xmeta);
02819 }
02820 iff.close_chunk();
02821 }
02822 }
02823 #endif
02824
02825
02826 #ifdef HAVE_NAMESPACES
02827 }
02828 # ifndef NOT_USING_DJVU_NAMESPACE
02829 using namespace DJVU;
02830 # endif
02831 #endif