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 "DjVuDocEditor.h"
00065 #include "DjVuImage.h"
00066 #include "IFFByteStream.h"
00067 #include "DataPool.h"
00068 #include "IW44Image.h"
00069 #include "GOS.h"
00070 #include "GURL.h"
00071 #include "DjVuAnno.h"
00072 #include "GRect.h"
00073 #include "DjVmNav.h"
00074
00075 #include "debug.h"
00076
00077 #include <ctype.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
00090 int DjVuDocEditor::thumbnails_per_file=10;
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102 class DjVuDocEditor::File : public GPEnabled
00103 {
00104 public:
00105
00106
00107
00108
00109
00110
00111 GP<DataPool> pool;
00112
00113
00114
00115
00116
00117 GP<DjVuFile> file;
00118 };
00119
00120 void
00121 DjVuDocEditor::check(void)
00122 {
00123 if (!initialized) G_THROW( ERR_MSG("DjVuDocEditor.not_init") );
00124 }
00125
00126 DjVuDocEditor::DjVuDocEditor(void)
00127 {
00128 initialized=false;
00129 refresh_cb=0;
00130 refresh_cl_data=0;
00131 }
00132
00133 DjVuDocEditor::~DjVuDocEditor(void)
00134 {
00135 if (!tmp_doc_url.is_empty())
00136 {
00137 tmp_doc_url.deletefile();
00138 }
00139
00140 GCriticalSectionLock lock(&thumb_lock);
00141 thumb_map.empty();
00142 DataPool::close_all();
00143 }
00144
00145 void
00146 DjVuDocEditor::init(void)
00147 {
00148 DEBUG_MSG("DjVuDocEditor::init() called\n");
00149 DEBUG_MAKE_INDENT(3);
00150
00151
00152 if (initialized) G_THROW( ERR_MSG("DjVuDocEditor.init") );
00153
00154 doc_url=GURL::Filename::UTF8("noname.djvu");
00155
00156 const GP<DjVmDoc> doc(DjVmDoc::create());
00157 const GP<ByteStream> gstr(ByteStream::create());
00158 doc->write(gstr);
00159 gstr->seek(0, SEEK_SET);
00160 doc_pool=DataPool::create(gstr);
00161
00162 orig_doc_type=UNKNOWN_TYPE;
00163 orig_doc_pages=0;
00164
00165 initialized=true;
00166
00167 DjVuDocument::init(doc_url, this);
00168 }
00169
00170 void
00171 DjVuDocEditor::init(const GURL &url)
00172 {
00173 DEBUG_MSG("DjVuDocEditor::init() called: url='" << url << "'\n");
00174 DEBUG_MAKE_INDENT(3);
00175
00176
00177 if (initialized)
00178 G_THROW( ERR_MSG("DjVuDocEditor.init") );
00179
00180
00181 doc_pool=DataPool::create(url);
00182 doc_url=url;
00183 const GP<DjVuDocument> tmp_doc(DjVuDocument::create_wait(doc_url,this));
00184 if (!tmp_doc->is_init_ok())
00185 G_THROW( ERR_MSG("DjVuDocEditor.open_fail") "\t" +url.get_string());
00186
00187 orig_doc_type=tmp_doc->get_doc_type();
00188 orig_doc_pages=tmp_doc->get_pages_num();
00189 if (orig_doc_type==OLD_BUNDLED ||
00190 orig_doc_type==OLD_INDEXED ||
00191 orig_doc_type==SINGLE_PAGE)
00192 {
00193
00194
00195 tmp_doc_url=GURL::Filename::Native(tmpnam(0));
00196 const GP<ByteStream> gstr(ByteStream::create(tmp_doc_url, "wb"));
00197 tmp_doc->write(gstr, true);
00198 gstr->flush();
00199 doc_pool=DataPool::create(tmp_doc_url);
00200 }
00201
00202
00203
00204
00205
00206
00207 initialized=true;
00208 DjVuDocument::init(doc_url, this);
00209
00210
00211 GCriticalSectionLock lock(&thumb_lock);
00212 int pages_num=get_pages_num();
00213 for(int page_num=0;page_num<pages_num;page_num++)
00214 {
00215
00216
00217
00218 const GP<DataPool> pool(DjVuDocument::get_thumbnail(page_num, true));
00219 if (pool)
00220 {
00221 thumb_map[page_to_id(page_num)]=pool;
00222 }
00223 }
00224
00225
00226 unfile_thumbnails();
00227 }
00228
00229 GP<DataPool>
00230 DjVuDocEditor::request_data(const DjVuPort * source, const GURL & url)
00231 {
00232 DEBUG_MSG("DjVuDocEditor::request_data(): url='" << url << "'\n");
00233 DEBUG_MAKE_INDENT(3);
00234
00235
00236
00237 if (url==doc_url)
00238 return doc_pool;
00239
00240
00241 const GP<DjVmDir::File> frec(djvm_dir->name_to_file(url.fname()));
00242 if (frec)
00243 {
00244 GCriticalSectionLock lock(&files_lock);
00245 GPosition pos;
00246 if (files_map.contains(frec->get_load_name(), pos))
00247 {
00248 const GP<File> f(files_map[pos]);
00249 if (f->file && f->file->get_init_data_pool())
00250 return f->file->get_init_data_pool();
00251 else if (f->pool) return f->pool;
00252 }
00253 }
00254
00255
00256
00257
00258 const GP<DataPool> pool(DjVuDocument::request_data(source, url));
00259
00260
00261
00262
00263
00264 return pool;
00265 }
00266
00267 void
00268 DjVuDocEditor::clean_files_map(void)
00269
00270
00271
00272 {
00273 DEBUG_MSG("DjVuDocEditor::clean_files_map() called\n");
00274 DEBUG_MAKE_INDENT(3);
00275
00276 GCriticalSectionLock lock(&files_lock);
00277
00278
00279
00280
00281
00282
00283 for(GPosition pos=files_map;pos;)
00284 {
00285 const GP<File> f(files_map[pos]);
00286 if (f->file && f->file->get_count()==1)
00287 {
00288 DEBUG_MSG("ZEROing file '" << f->file->get_url() << "'\n");
00289 if (f->file->is_modified())
00290 f->pool=f->file->get_djvu_data(false);
00291 f->file=0;
00292 }
00293 if (!f->file && !f->pool)
00294 {
00295 DEBUG_MSG("Removing record '" << files_map.key(pos) << "'\n");
00296 GPosition this_pos=pos;
00297 ++pos;
00298 files_map.del(this_pos);
00299 } else ++pos;
00300 }
00301 }
00302
00303 GP<DjVuFile>
00304 DjVuDocEditor::url_to_file(const GURL & url, bool dont_create) const
00305 {
00306 DEBUG_MSG("DjVuDocEditor::url_to_file(): url='" << url << "'\n");
00307 DEBUG_MAKE_INDENT(3);
00308
00309
00310
00311 GP<DjVmDir::File> frec;
00312 if((const DjVmDir *)djvm_dir)
00313 frec=djvm_dir->name_to_file(url.fname());
00314 if (frec)
00315 {
00316 GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock));
00317 GPosition pos;
00318 if (files_map.contains(frec->get_load_name(), pos))
00319 {
00320 const GP<File> f(files_map[pos]);
00321 if (f->file)
00322 return f->file;
00323 }
00324 }
00325
00326 const_cast<DjVuDocEditor *>(this)->clean_files_map();
00327
00328
00329 const GP<DjVuFile> file(DjVuDocument::url_to_file(url, dont_create));
00330
00331
00332 if (file && frec)
00333 {
00334 GCriticalSectionLock lock(&(const_cast<DjVuDocEditor *>(this)->files_lock));
00335 GPosition pos;
00336 if (files_map.contains(frec->get_load_name(), pos))
00337 {
00338 files_map[frec->get_load_name()]->file=file;
00339 }else
00340 {
00341 const GP<File> f(new File());
00342 f->file=file;
00343 const_cast<DjVuDocEditor *>(this)->files_map[frec->get_load_name()]=f;
00344 }
00345 }
00346
00347 return file;
00348 }
00349
00350 GUTF8String
00351 DjVuDocEditor::page_to_id(int page_num) const
00352 {
00353 if (page_num<0 || page_num>=get_pages_num())
00354 G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num));
00355 const GP<DjVmDir::File> f(djvm_dir->page_to_file(page_num));
00356 if (! f)
00357 G_THROW( ERR_MSG("DjVuDocEditor.page_num") "\t"+GUTF8String(page_num));
00358
00359 return f->get_load_name();
00360 }
00361
00362 GUTF8String
00363 DjVuDocEditor::find_unique_id(GUTF8String id)
00364 {
00365 const GP<DjVmDir> dir(get_djvm_dir());
00366
00367 GUTF8String base, ext;
00368 const int dot=id.rsearch('.');
00369 if(dot >= 0)
00370 {
00371 base=id.substr(0,dot);
00372 ext=id.substr(dot+1,(unsigned int)-1);
00373 }else
00374 {
00375 base=id;
00376 }
00377
00378 int cnt=0;
00379 while (!(!dir->id_to_file(id) &&
00380 !dir->name_to_file(id) &&
00381 !dir->title_to_file(id)))
00382 {
00383 cnt++;
00384 id=base+"_"+GUTF8String(cnt);
00385 if (ext.length())
00386 id+="."+ext;
00387 }
00388 return id;
00389 }
00390
00391 GP<DataPool>
00392 DjVuDocEditor::strip_incl_chunks(const GP<DataPool> & pool_in)
00393 {
00394 DEBUG_MSG("DjVuDocEditor::strip_incl_chunks() called\n");
00395 DEBUG_MAKE_INDENT(3);
00396
00397 const GP<IFFByteStream> giff_in(
00398 IFFByteStream::create(pool_in->get_stream()));
00399
00400 const GP<ByteStream> gbs_out(ByteStream::create());
00401 const GP<IFFByteStream> giff_out(IFFByteStream::create(gbs_out));
00402
00403 IFFByteStream &iff_in=*giff_in;
00404 IFFByteStream &iff_out=*giff_out;
00405
00406 bool have_incl=false;
00407 int chksize;
00408 GUTF8String chkid;
00409 if (iff_in.get_chunk(chkid))
00410 {
00411 iff_out.put_chunk(chkid);
00412 while((chksize=iff_in.get_chunk(chkid)))
00413 {
00414 if (chkid!="INCL")
00415 {
00416 iff_out.put_chunk(chkid);
00417 iff_out.copy(*iff_in.get_bytestream());
00418 iff_out.close_chunk();
00419 } else
00420 {
00421 have_incl=true;
00422 }
00423 iff_in.close_chunk();
00424 }
00425 iff_out.close_chunk();
00426 }
00427
00428 if (have_incl)
00429 {
00430 gbs_out->seek(0,SEEK_SET);
00431 return DataPool::create(gbs_out);
00432 } else return pool_in;
00433 }
00434
00435 GUTF8String
00436 DjVuDocEditor::insert_file(const GURL &file_url, const GUTF8String &parent_id,
00437 int chunk_num, DjVuPort *source)
00438
00439
00440
00441
00442 {
00443 DEBUG_MSG("DjVuDocEditor::insert_file(): fname='" << file_url <<
00444 "', parent_id='" << parent_id << "'\n");
00445 DEBUG_MAKE_INDENT(3);
00446 const GP<DjVmDir> dir(get_djvm_dir());
00447
00448 if(!source)
00449 source=this;
00450
00451 GP<DataPool> file_pool;
00452 if(file_url.is_empty()||file_url.is_local_file_url())
00453 {
00454 file_pool=DataPool::create(file_url);
00455 }else
00456 {
00457 file_pool=source->request_data(source, file_url);
00458 if(source != this)
00459 {
00460 file_pool=DataPool::create(file_pool->get_stream()->duplicate());
00461 }
00462 }
00463 if(file_pool && file_url && DjVuDocument::djvu_import_codec)
00464 {
00465 (*DjVuDocument::djvu_import_codec)(file_pool,file_url,needs_compression_flag,can_compress_flag);
00466 }
00467
00468
00469 file_pool=strip_incl_chunks(file_pool);
00470
00471
00472 GP<DjVmDir::File> parent_frec(dir->id_to_file(parent_id));
00473 if (!parent_frec)
00474 parent_frec=dir->name_to_file(parent_id);
00475 if (!parent_frec)
00476 parent_frec=dir->title_to_file(parent_id);
00477 if (!parent_frec)
00478 G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t" +parent_id);
00479 const GP<DjVuFile> parent_file(get_djvu_file(parent_id));
00480 if (!parent_file)
00481 G_THROW( ERR_MSG("DjVuDocEditor.create_fail") "\t"+parent_id);
00482
00483
00484 const GUTF8String id(find_unique_id(file_url.fname()));
00485
00486
00487 const GP<DjVmDir::File> frec(
00488 DjVmDir::File::create(id, id, id, DjVmDir::File::INCLUDE));
00489 int pos=dir->get_file_pos(parent_frec);
00490 if (pos>=0)
00491 ++pos;
00492 dir->insert_file(frec, pos);
00493
00494
00495 {
00496 const GP<File> f(new File);
00497 f->pool=file_pool;
00498 GCriticalSectionLock lock(&files_lock);
00499 files_map[id]=f;
00500 }
00501
00502
00503 parent_file->insert_file(id, chunk_num);
00504
00505 return id;
00506 }
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525 bool
00526 DjVuDocEditor::insert_file(const GURL &file_url, bool is_page,
00527 int & file_pos, GMap<GUTF8String, GUTF8String> & name2id,
00528 DjVuPort *source)
00529 {
00530
00531 DEBUG_MSG("DjVuDocEditor::insert_file(): file_url='" << file_url <<
00532 "', is_page='" << is_page << "'\n");
00533 DEBUG_MAKE_INDENT(3);
00534 if (refresh_cb)
00535 refresh_cb(refresh_cl_data);
00536
00537
00538
00539
00540
00541 if (name2id.contains(file_url.fname()))
00542 return true;
00543
00544 if(!source)
00545 source=this;
00546
00547 GP<DataPool> file_pool;
00548 if(file_url.is_empty()||file_url.is_local_file_url())
00549 {
00550 file_pool=DataPool::create(file_url);
00551 }
00552 else
00553 {
00554 file_pool=source->request_data(source, file_url);
00555 if(source != this)
00556 {
00557 file_pool=DataPool::create(file_pool->get_stream());
00558 }
00559 }
00560
00561 if(file_pool && !file_url.is_empty() && DjVuDocument::djvu_import_codec)
00562 {
00563 (*DjVuDocument::djvu_import_codec)(file_pool,file_url,
00564 needs_compression_flag,
00565 can_compress_flag);
00566 }
00567
00568
00569 {
00570 const GP<IFFByteStream> giff(
00571 IFFByteStream::create(file_pool->get_stream()));
00572 IFFByteStream &iff=*giff;
00573 GUTF8String chkid;
00574
00575 int length;
00576 length=iff.get_chunk(chkid);
00577 if (chkid!="FORM:DJVI" && chkid!="FORM:DJVU" &&
00578 chkid!="FORM:BM44" && chkid!="FORM:PM44")
00579 G_THROW( ERR_MSG("DjVuDocEditor.not_1_page") "\t"+file_url.get_string());
00580
00581
00582
00583 while(iff.get_chunk(chkid))
00584 {
00585 if (chkid=="NDIR")
00586 return false;
00587 iff.close_chunk();
00588 }
00589 }
00590 return insert_file(file_pool,file_url,is_page,file_pos,name2id,source);
00591 }
00592
00593 bool
00594 DjVuDocEditor::insert_file(const GP<DataPool> &file_pool,
00595 const GURL &file_url, bool is_page,
00596 int & file_pos, GMap<GUTF8String, GUTF8String> & name2id,
00597 DjVuPort *source)
00598 {
00599 GUTF8String errors;
00600 if(file_pool)
00601 {
00602 const GP<DjVmDir> dir(get_djvm_dir());
00603 G_TRY
00604 {
00605
00606
00607 const GUTF8String name=file_url.fname();
00608 GUTF8String id;
00609 if (name2id.contains(name))
00610 {
00611 id=name2id[name];
00612 }else
00613 {
00614
00615 if(!is_page)
00616 {
00617 GPList<DjVmDir::File> list(dir->get_files_list());
00618 for(GPosition pos=list;pos;++pos)
00619 {
00620 DEBUG_MSG("include " << list[pos]->is_include()
00621 << " size=" << list[pos]->size << " length="
00622 << file_pool->get_length() << "\n");
00623 if(list[pos]->is_include()
00624 && (!list[pos]->size
00625 || (list[pos]->size == file_pool->get_length())))
00626 {
00627 id=list[pos]->get_load_name();
00628 GP<DjVuFile> file(get_djvu_file(id,false));
00629 const GP<DataPool> pool(file->get_djvu_data(false));
00630 if(file_pool->simple_compare(*pool))
00631 {
00632
00633 name2id[name]=id;
00634 }
00635 const GP<IFFByteStream> giff_old(IFFByteStream::create(pool->get_stream()));
00636 const GP<IFFByteStream> giff_new(IFFByteStream::create(file_pool->get_stream()));
00637 file=0;
00638 if(giff_old->compare(*giff_new))
00639 {
00640
00641 name2id[name]=id;
00642 return true;
00643 }
00644 }
00645 }
00646 }
00647
00648 id=find_unique_id(name);
00649 name2id[name]=id;
00650 }
00651
00652
00653
00654
00655
00656
00657
00658 const GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id,
00659 is_page ? DjVmDir::File::PAGE : DjVmDir::File::INCLUDE ));
00660
00661
00662 file_pos=dir->insert_file(file, file_pos);
00663
00664
00665 {
00666 const GP<File> f(new File);
00667 f->pool=file_pool;
00668 GCriticalSectionLock lock(&files_lock);
00669 files_map[id]=f;
00670 }
00671
00672
00673
00674
00675
00676
00677
00678 GUTF8String chkid;
00679 const GP<IFFByteStream> giff_in(
00680 IFFByteStream::create(file_pool->get_stream()));
00681 IFFByteStream &iff_in=*giff_in;
00682 const GP<ByteStream> gstr_out(ByteStream::create());
00683 const GP<IFFByteStream> giff_out(IFFByteStream::create(gstr_out));
00684 IFFByteStream &iff_out=*giff_out;
00685
00686 const GP<DjVmDir::File> shared_frec(djvm_dir->get_shared_anno_file());
00687
00688 iff_in.get_chunk(chkid);
00689 iff_out.put_chunk(chkid);
00690 while(iff_in.get_chunk(chkid))
00691 {
00692 if (chkid!="INCL")
00693 {
00694 iff_out.put_chunk(chkid);
00695 iff_out.copy(*iff_in.get_bytestream());
00696 iff_in.close_chunk();
00697 iff_out.close_chunk();
00698 if (shared_frec && chkid=="INFO")
00699 {
00700 iff_out.put_chunk("INCL");
00701 iff_out.get_bytestream()->writestring(shared_frec->get_load_name());
00702 iff_out.close_chunk();
00703 }
00704 } else
00705 {
00706 GUTF8String name;
00707 char buffer[1024];
00708 int length;
00709 while((length=iff_in.read(buffer, 1024)))
00710 name+=GUTF8String(buffer, length);
00711 while(isspace(name[0]))
00712 {
00713 name=name.substr(1,(unsigned int)-1);
00714 }
00715 while(isspace(name[(int)name.length()-1]))
00716 {
00717 name.setat(name.length()-1, 0);
00718 }
00719 const GURL::UTF8 full_url(name,file_url.base());
00720 iff_in.close_chunk();
00721
00722 G_TRY {
00723 if (insert_file(full_url, false, file_pos, name2id, source))
00724 {
00725
00726
00727 GUTF8String id=name2id[name];
00728 iff_out.put_chunk("INCL");
00729 iff_out.get_bytestream()->writestring(id);
00730 iff_out.close_chunk();
00731 }
00732 } G_CATCH(exc) {
00733
00734
00735 if (errors.length())
00736 errors+="\n\n";
00737 errors+=exc.get_cause();
00738 } G_ENDCATCH;
00739 }
00740 }
00741 iff_out.close_chunk();
00742
00743
00744 if (file_pos>=0) file_pos++;
00745
00746
00747
00748 gstr_out->seek(0);
00749 const GP<DataPool> new_file_pool(DataPool::create(gstr_out));
00750 {
00751
00752
00753
00754
00755 GCriticalSectionLock lock(&files_lock);
00756 files_map[id]->pool=new_file_pool;
00757 }
00758 } G_CATCH(exc) {
00759 if (errors.length())
00760 errors+="\n\n";
00761 errors+=exc.get_cause();
00762 G_THROW(errors);
00763 } G_ENDCATCH;
00764
00765
00766
00767
00768 if (errors.length())
00769 G_THROW(errors);
00770
00771 return true;
00772 }
00773 return false;
00774 }
00775
00776 void
00777 DjVuDocEditor::insert_group(const GList<GURL> & file_urls, int page_num,
00778 void (* _refresh_cb)(void *), void * _cl_data)
00779
00780
00781
00782 {
00783 refresh_cb=_refresh_cb;
00784 refresh_cl_data=_cl_data;
00785
00786 G_TRY
00787 {
00788
00789
00790 const GP<DjVmDir> dir(get_djvm_dir());
00791 int file_pos;
00792 if (page_num<0 || page_num>=dir->get_pages_num())
00793 {
00794 file_pos=-1;
00795 }
00796 else
00797 {
00798 file_pos=dir->get_page_pos(page_num);
00799 }
00800
00801
00802
00803
00804 GMap<GUTF8String, GUTF8String> name2id;
00805
00806 GUTF8String errors;
00807 for(GPosition pos=file_urls;pos;++pos)
00808 {
00809 const GURL &furl=file_urls[pos];
00810 DEBUG_MSG( "Inserting file '" << furl << "'\n" );
00811 G_TRY
00812 {
00813
00814 GP<DataPool> xdata_pool(DataPool::create(furl));
00815 if(xdata_pool && furl.is_valid()
00816 && furl.is_local_file_url() && DjVuDocument::djvu_import_codec)
00817 {
00818 (*DjVuDocument::djvu_import_codec)(xdata_pool,furl,
00819 needs_compression_flag,
00820 can_compress_flag);
00821 }
00822 GUTF8String chkid;
00823 IFFByteStream::create(xdata_pool->get_stream())->get_chunk(chkid);
00824 if (name2id.contains(furl.fname())||(chkid=="FORM:DJVM"))
00825 {
00826 GMap<GUTF8String,void *> map;
00827 map_ids(map);
00828 DEBUG_MSG("Read DjVuDocument furl='" << furl << "'\n");
00829 GP<ByteStream> gbs(ByteStream::create());
00830 GP<DjVuDocument> doca(DjVuDocument::create_noinit());
00831 doca->set_verbose_eof(verbose_eof);
00832 doca->set_recover_errors(recover_errors);
00833 doca->init(furl );
00834 doca->wait_for_complete_init();
00835 get_portcaster()->add_route(doca,this);
00836 DEBUG_MSG("Saving DjVuDocument url='" << furl << "' with unique names\n");
00837 doca->write(gbs,map);
00838 gbs->seek(0L);
00839 DEBUG_MSG("Loading unique names\n");
00840 GP<DjVuDocument> doc(DjVuDocument::create(gbs));
00841 doc->set_verbose_eof(verbose_eof);
00842 doc->set_recover_errors(recover_errors);
00843 doc->wait_for_complete_init();
00844 get_portcaster()->add_route(doc,this);
00845 gbs=0;
00846 DEBUG_MSG("Inserting pages\n");
00847 int pages_num=doc->get_pages_num();
00848 for(int page_num=0;page_num<pages_num;page_num++)
00849 {
00850 const GURL url(doc->page_to_url(page_num));
00851 insert_file(url, true, file_pos, name2id, doc);
00852 }
00853 }
00854 else
00855 {
00856 insert_file(furl, true, file_pos, name2id, this);
00857 }
00858 } G_CATCH(exc)
00859 {
00860 if (errors.length())
00861 {
00862 errors+="\n\n";
00863 }
00864 errors+=exc.get_cause();
00865 }
00866 G_ENDCATCH;
00867 }
00868 if (errors.length())
00869 {
00870 G_THROW(errors);
00871 }
00872 } G_CATCH_ALL
00873 {
00874 refresh_cb=0;
00875 refresh_cl_data=0;
00876 G_RETHROW;
00877 } G_ENDCATCH;
00878 refresh_cb=0;
00879 refresh_cl_data=0;
00880 }
00881
00882 void
00883 DjVuDocEditor::insert_page(const GURL &file_url, int page_num)
00884 {
00885 DEBUG_MSG("DjVuDocEditor::insert_page(): furl='" << file_url << "'\n");
00886 DEBUG_MAKE_INDENT(3);
00887
00888 GList<GURL> list;
00889 list.append(file_url);
00890
00891 insert_group(list, page_num);
00892 }
00893
00894 void
00895 DjVuDocEditor::insert_page(GP<DataPool> & _file_pool,
00896 const GURL & file_url, int page_num)
00897
00898
00899 {
00900 DEBUG_MSG("DjVuDocEditor::insert_page(): pool size='" <<
00901 _file_pool->get_size() << "'\n");
00902 DEBUG_MAKE_INDENT(3);
00903
00904 const GP<DjVmDir> dir(get_djvm_dir());
00905
00906
00907
00908 const GP<DataPool> file_pool(strip_incl_chunks(_file_pool));
00909
00910
00911 const GUTF8String id(find_unique_id(file_url.fname()));
00912
00913
00914 const GP<DjVmDir::File> frec(DjVmDir::File::create(
00915 id, id, id, DjVmDir::File::PAGE));
00916 int pos=dir->get_page_pos(page_num);
00917 dir->insert_file(frec, pos);
00918
00919
00920 {
00921 GP<File> f=new File;
00922 f->pool=file_pool;
00923 GCriticalSectionLock lock(&files_lock);
00924 files_map[id]=f;
00925 }
00926 }
00927
00928 void
00929 DjVuDocEditor::generate_ref_map(const GP<DjVuFile> & file,
00930 GMap<GUTF8String, void *> & ref_map,
00931 GMap<GURL, void *> & visit_map)
00932
00933
00934
00935
00936 {
00937 const GURL url=file->get_url();
00938 const GUTF8String id(djvm_dir->name_to_file(url.fname())->get_load_name());
00939 if (!visit_map.contains(url))
00940 {
00941 visit_map[url]=0;
00942
00943 GPList<DjVuFile> files_list=file->get_included_files(false);
00944 for(GPosition pos=files_list;pos;++pos)
00945 {
00946 GP<DjVuFile> child_file=files_list[pos];
00947
00948
00949 GURL child_url=child_file->get_url();
00950 const GUTF8String child_id(
00951 djvm_dir->name_to_file(child_url.fname())->get_load_name());
00952 GMap<GUTF8String, void *> * parents=0;
00953 if (ref_map.contains(child_id))
00954 parents=(GMap<GUTF8String, void *> *) ref_map[child_id];
00955 else
00956 ref_map[child_id]=parents=new GMap<GUTF8String, void *>();
00957 (*parents)[id]=0;
00958
00959 generate_ref_map(child_file, ref_map, visit_map);
00960 }
00961 }
00962 }
00963
00964 void
00965 DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref,
00966 GMap<GUTF8String, void *> & ref_map)
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977 {
00978
00979 GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[id];
00980 if (parents)
00981 {
00982 for(GPosition pos=*parents;pos;++pos)
00983 {
00984 const GUTF8String parent_id((*parents).key(pos));
00985 const GP<DjVuFile> parent(get_djvu_file(parent_id));
00986 if (parent)
00987 parent->unlink_file(id);
00988 }
00989 delete parents;
00990 parents=0;
00991 ref_map.del(id);
00992 }
00993
00994
00995 GUTF8String errors;
00996
00997
00998 GP<DjVuFile> file=get_djvu_file(id);
00999 if (file)
01000 {
01001 G_TRY {
01002 GPList<DjVuFile> files_list=file->get_included_files(false);
01003 for(GPosition pos=files_list;pos;++pos)
01004 {
01005 GP<DjVuFile> child_file=files_list[pos];
01006 GURL child_url=child_file->get_url();
01007 const GUTF8String child_id(
01008 djvm_dir->name_to_file(child_url.fname())->get_load_name());
01009 GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[child_id];
01010 if (parents) parents->del(id);
01011
01012 if (remove_unref && (!parents || !parents->size()))
01013 remove_file(child_id, remove_unref, ref_map);
01014 }
01015 } G_CATCH(exc) {
01016 if (errors.length()) errors+="\n\n";
01017 errors+=exc.get_cause();
01018 } G_ENDCATCH;
01019 }
01020
01021
01022 djvm_dir->delete_file(id);
01023
01024
01025 GCriticalSectionLock lock(&thumb_lock);
01026 GPosition pos(thumb_map.contains(id));
01027 if (pos)
01028 {
01029 thumb_map.del(pos);
01030 }
01031 if (errors.length())
01032 G_THROW(errors);
01033 }
01034
01035 void
01036 DjVuDocEditor::remove_file(const GUTF8String &id, bool remove_unref)
01037 {
01038 DEBUG_MSG("DjVuDocEditor::remove_file(): id='" << id << "'\n");
01039 DEBUG_MAKE_INDENT(3);
01040
01041 if (!djvm_dir->id_to_file(id))
01042 G_THROW( ERR_MSG("DjVuDocEditor.no_file") "\t"+id);
01043
01044
01045
01046
01047 GMap<GUTF8String, void *> ref_map;
01048 GMap<GURL, void *> visit_map;
01049
01050 int pages_num=djvm_dir->get_pages_num();
01051 for(int page_num=0;page_num<pages_num;page_num++)
01052 generate_ref_map(get_djvu_file(page_num), ref_map, visit_map);
01053
01054
01055 remove_file(id, remove_unref, ref_map);
01056
01057
01058 GPosition pos;
01059 while((pos=ref_map))
01060 {
01061 GMap<GUTF8String, void *> * parents=(GMap<GUTF8String, void *> *) ref_map[pos];
01062 delete parents;
01063 ref_map.del(pos);
01064 }
01065 }
01066
01067 void
01068 DjVuDocEditor::remove_page(int page_num, bool remove_unref)
01069 {
01070 DEBUG_MSG("DjVuDocEditor::remove_page(): page_num=" << page_num << "\n");
01071 DEBUG_MAKE_INDENT(3);
01072
01073
01074 GP<DjVmDir> djvm_dir=get_djvm_dir();
01075 if (page_num<0 || page_num>=djvm_dir->get_pages_num())
01076 G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
01077
01078
01079 remove_file(djvm_dir->page_to_file(page_num)->get_load_name(), remove_unref);
01080 }
01081
01082 void
01083 DjVuDocEditor::remove_pages(const GList<int> & page_list, bool remove_unref)
01084 {
01085 DEBUG_MSG("DjVuDocEditor::remove_pages() called\n");
01086 DEBUG_MAKE_INDENT(3);
01087
01088
01089
01090 GP<DjVmDir> djvm_dir=get_djvm_dir();
01091 GPosition pos ;
01092 if (djvm_dir)
01093 {
01094 GList<GUTF8String> id_list;
01095 for(pos=page_list;pos;++pos)
01096 {
01097 GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
01098 if (frec)
01099 id_list.append(frec->get_load_name());
01100 }
01101
01102 for(pos=id_list;pos;++pos)
01103 {
01104 GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
01105 if (frec)
01106 remove_page(frec->get_page_num(), remove_unref);
01107 }
01108 }
01109 }
01110
01111 void
01112 DjVuDocEditor::move_file(const GUTF8String &id, int & file_pos,
01113 GMap<GUTF8String, void *> & map)
01114
01115
01116 {
01117 if (!map.contains(id))
01118 {
01119 map[id]=0;
01120
01121 GP<DjVmDir::File> file_rec=djvm_dir->id_to_file(id);
01122 if (file_rec)
01123 {
01124 file_rec=new DjVmDir::File(*file_rec);
01125 djvm_dir->delete_file(id);
01126 djvm_dir->insert_file(file_rec, file_pos);
01127
01128 if (file_pos>=0)
01129 {
01130 file_pos++;
01131
01132
01133
01134
01135
01136
01137
01138 GP<DjVuFile> djvu_file=get_djvu_file(id);
01139 if (djvu_file)
01140 {
01141 GPList<DjVuFile> files_list=djvu_file->get_included_files(false);
01142 for(GPosition pos=files_list;pos;++pos)
01143 {
01144 const GUTF8String name(files_list[pos]->get_url().fname());
01145 GP<DjVmDir::File> child_frec=djvm_dir->name_to_file(name);
01146
01147
01148
01149
01150 if (child_frec)
01151 if (djvm_dir->get_file_pos(child_frec)>file_pos)
01152 move_file(child_frec->get_load_name(), file_pos, map);
01153 }
01154 }
01155 }
01156 }
01157 }
01158 }
01159
01160 void
01161 DjVuDocEditor::move_page(int page_num, int new_page_num)
01162 {
01163 DEBUG_MSG("DjVuDocEditor::move_page(): page_num=" << page_num <<
01164 ", new_page_num=" << new_page_num << "\n");
01165 DEBUG_MAKE_INDENT(3);
01166
01167 if (page_num==new_page_num) return;
01168
01169 int pages_num=get_pages_num();
01170 if (page_num<0 || page_num>=pages_num)
01171 G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
01172
01173 const GUTF8String id(page_to_id(page_num));
01174 int file_pos=-1;
01175 if (new_page_num>=0 && new_page_num<pages_num)
01176 if (new_page_num>page_num)
01177 {
01178 if (new_page_num<pages_num-1)
01179 file_pos=djvm_dir->get_page_pos(new_page_num+1)-1;
01180 } else
01181 file_pos=djvm_dir->get_page_pos(new_page_num);
01182
01183 GMap<GUTF8String, void *> map;
01184 move_file(id, file_pos, map);
01185 }
01186 #ifdef _WIN32_WCE_EMULATION // Work around odd behavior under WCE Emulation
01187 #define CALLINGCONVENTION __cdecl
01188 #else
01189 #define CALLINGCONVENTION
01190 #endif
01191
01192 static int
01193 CALLINGCONVENTION
01194 cmp(const void * ptr1, const void * ptr2)
01195 {
01196 int num1=*(int *) ptr1;
01197 int num2=*(int *) ptr2;
01198 return num1<num2 ? -1 : num1>num2 ? 1 : 0;
01199 }
01200
01201 static GList<int>
01202 sortList(const GList<int> & list)
01203 {
01204 GArray<int> a(list.size()-1);
01205 int cnt;
01206 GPosition pos;
01207 for(pos=list, cnt=0;pos;++pos, cnt++)
01208 a[cnt]=list[pos];
01209
01210 qsort((int *) a, a.size(), sizeof(int), cmp);
01211
01212 GList<int> l;
01213 for(int i=0;i<a.size();i++)
01214 l.append(a[i]);
01215
01216 return l;
01217 }
01218
01219 void
01220 DjVuDocEditor::move_pages(const GList<int> & _page_list, int shift)
01221 {
01222 if (!shift) return;
01223
01224 GList<int> page_list=sortList(_page_list);
01225
01226 GList<GUTF8String> id_list;
01227 for(GPosition pos=page_list;pos;++pos)
01228 {
01229 GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
01230 if (frec)
01231 id_list.append(frec->get_load_name());
01232 }
01233
01234 if (shift<0)
01235 {
01236
01237
01238
01239
01240
01241
01242 int min_page=0;
01243 for(GPosition pos=id_list;pos;++pos)
01244 {
01245 GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
01246 if (frec)
01247 {
01248 int page_num=frec->get_page_num();
01249 int new_page_num=page_num+shift;
01250 if (new_page_num<min_page)
01251 new_page_num=min_page++;
01252 move_page(page_num, new_page_num);
01253 }
01254 }
01255 } else
01256 {
01257
01258
01259
01260
01261
01262
01263 int max_page=djvm_dir->get_pages_num()-1;
01264 for(GPosition pos=id_list.lastpos();pos;--pos)
01265 {
01266 GP<DjVmDir::File> frec=djvm_dir->id_to_file(id_list[pos]);
01267 if (frec)
01268 {
01269 int page_num=frec->get_page_num();
01270 int new_page_num=page_num+shift;
01271 if (new_page_num>max_page)
01272 new_page_num=max_page--;
01273 move_page(page_num, new_page_num);
01274 }
01275 }
01276 }
01277 }
01278
01279 void
01280 DjVuDocEditor::set_file_name(const GUTF8String &id, const GUTF8String &name)
01281 {
01282 DEBUG_MSG("DjVuDocEditor::set_file_name(), id='" << id << "', name='" << name << "'\n");
01283 DEBUG_MAKE_INDENT(3);
01284
01285
01286
01287 GURL url=id_to_url(id);
01288
01289
01290 djvm_dir->set_file_name(id, name);
01291
01292
01293 GPosition pos;
01294 if (files_map.contains(id, pos))
01295 {
01296 GP<File> file=files_map[pos];
01297 GP<DataPool> pool=file->pool;
01298 if (pool) pool->load_file();
01299 GP<DjVuFile> djvu_file=file->file;
01300 if (djvu_file) djvu_file->set_name(name);
01301 }
01302 }
01303
01304 void
01305 DjVuDocEditor::set_page_name(int page_num, const GUTF8String &name)
01306 {
01307 DEBUG_MSG("DjVuDocEditor::set_page_name(), page_num='" << page_num << "'\n");
01308 DEBUG_MAKE_INDENT(3);
01309
01310 if (page_num<0 || page_num>=get_pages_num())
01311 G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
01312
01313 set_file_name(page_to_id(page_num), name);
01314 }
01315
01316 void
01317 DjVuDocEditor::set_file_title(const GUTF8String &id, const GUTF8String &title)
01318 {
01319 DEBUG_MSG("DjVuDocEditor::set_file_title(), id='" << id << "', title='" << title << "'\n");
01320 DEBUG_MAKE_INDENT(3);
01321
01322
01323 djvm_dir->set_file_title(id, title);
01324 }
01325
01326 void
01327 DjVuDocEditor::set_page_title(int page_num, const GUTF8String &title)
01328 {
01329 DEBUG_MSG("DjVuDocEditor::set_page_title(), page_num='" << page_num << "'\n");
01330 DEBUG_MAKE_INDENT(3);
01331
01332 if (page_num<0 || page_num>=get_pages_num())
01333 G_THROW( ERR_MSG("DjVuDocEditor.bad_page") "\t"+GUTF8String(page_num));
01334
01335 set_file_title(page_to_id(page_num), title);
01336 }
01337
01338
01339
01340
01341
01342 void
01343 DjVuDocEditor::simplify_anno(void (* progress_cb)(float progress, void *),
01344 void * cl_data)
01345
01346
01347
01348
01349 {
01350
01351
01352 GP<DjVmDir::File> shared_file=djvm_dir->get_shared_anno_file();
01353 GUTF8String shared_id;
01354 if (shared_file)
01355 shared_id=shared_file->get_load_name();
01356
01357 GList<GURL> ignore_list;
01358 if (shared_id.length())
01359 ignore_list.append(id_to_url(shared_id));
01360
01361
01362
01363 int pages_num=djvm_dir->get_pages_num();
01364 for(int page_num=0;page_num<pages_num;page_num++)
01365 {
01366 GP<DjVuFile> djvu_file=get_djvu_file(page_num);
01367 if (!djvu_file)
01368 G_THROW( ERR_MSG("DjVuDocEditor.page_fail") "\t"+page_num);
01369 int max_level=0;
01370 GP<ByteStream> anno;
01371 anno=djvu_file->get_merged_anno(ignore_list, &max_level);
01372 if (anno && max_level>0)
01373 {
01374
01375
01376 GSafeFlags & file_flags=djvu_file->get_safe_flags();
01377 GMonitorLock lock(&file_flags);
01378 while(file_flags & DjVuFile::DECODING)
01379 file_flags.wait();
01380
01381
01382 const GP<DjVuAnno> dec_anno(DjVuAnno::create());
01383 dec_anno->decode(anno);
01384 const GP<ByteStream> new_anno(ByteStream::create());
01385 dec_anno->encode(new_anno);
01386 new_anno->seek(0);
01387
01388
01389 djvu_file->anno=new_anno;
01390 djvu_file->rebuild_data_pool();
01391 if ((file_flags & (DjVuFile::DECODE_OK |
01392 DjVuFile::DECODE_FAILED |
01393 DjVuFile::DECODE_STOPPED))==0)
01394 djvu_file->anno=0;
01395 }
01396 if (progress_cb)
01397 progress_cb((float)(page_num/2.0/pages_num), cl_data);
01398 }
01399
01400
01401
01402
01403 GPList<DjVmDir::File> files_list=djvm_dir->get_files_list();
01404 int cnt;
01405 GPosition pos;
01406 for(pos=files_list, cnt=0;pos;++pos, cnt++)
01407 {
01408 GP<DjVmDir::File> frec=files_list[pos];
01409 if (!frec->is_page() && frec->get_load_name()!=shared_id)
01410 {
01411 GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name());
01412 if (djvu_file)
01413 {
01414 djvu_file->remove_anno();
01415 if (djvu_file->get_chunks_number()==0)
01416 remove_file(frec->get_load_name(), true);
01417 }
01418 }
01419 if (progress_cb)
01420 progress_cb((float)(0.5+cnt/2.0/files_list.size()), cl_data);
01421 }
01422 }
01423
01424 void
01425 DjVuDocEditor::create_shared_anno_file(void (* progress_cb)(float progress, void *),
01426 void * cl_data)
01427 {
01428 if (djvm_dir->get_shared_anno_file())
01429 G_THROW( ERR_MSG("DjVuDocEditor.share_fail") );
01430
01431
01432 const GP<ByteStream> gstr(ByteStream::create());
01433 const GP<IFFByteStream> giff(IFFByteStream::create(gstr));
01434 IFFByteStream &iff=*giff;
01435 iff.put_chunk("FORM:DJVI");
01436 iff.put_chunk("ANTa");
01437 iff.close_chunk();
01438 iff.close_chunk();
01439 ByteStream &str=*gstr;
01440 str.flush();
01441 str.seek(0);
01442 const GP<DataPool> file_pool(DataPool::create(gstr));
01443
01444
01445 const GUTF8String id(find_unique_id("shared_anno.iff"));
01446
01447
01448 GP<DjVmDir::File> frec(DjVmDir::File::create(id, id, id,
01449 DjVmDir::File::SHARED_ANNO));
01450 djvm_dir->insert_file(frec, 1);
01451
01452
01453 {
01454 GP<File> f=new File;
01455 f->pool=file_pool;
01456 GCriticalSectionLock lock(&files_lock);
01457 files_map[id]=f;
01458 }
01459
01460
01461 int pages_num=djvm_dir->get_pages_num();
01462 for(int page_num=0;page_num<pages_num;page_num++)
01463 {
01464 GP<DjVuFile> djvu_file=get_djvu_file(page_num);
01465 djvu_file->insert_file(id, 1);
01466
01467 if (progress_cb)
01468 progress_cb((float) page_num/pages_num, cl_data);
01469 }
01470 }
01471
01472 void
01473 DjVuDocEditor::set_djvm_nav(GP<DjVmNav> n)
01474 {
01475 if (n && ! n->isValidBookmark())
01476 G_THROW("Invalid bookmark data");
01477 djvm_nav = n;
01478 }
01479
01480 GP<DjVuFile>
01481 DjVuDocEditor::get_shared_anno_file(void)
01482 {
01483 GP<DjVuFile> djvu_file;
01484
01485 GP<DjVmDir::File> frec=djvm_dir->get_shared_anno_file();
01486 if (frec)
01487 djvu_file=get_djvu_file(frec->get_load_name());
01488
01489 return djvu_file;
01490 }
01491
01492 GP<DataPool>
01493 DjVuDocEditor::get_thumbnail(int page_num, bool dont_decode)
01494
01495
01496
01497
01498
01499
01500
01501
01502 {
01503 const GUTF8String id(page_to_id(page_num));
01504
01505 GCriticalSectionLock lock(&thumb_lock);
01506 const GPosition pos(thumb_map.contains(id));
01507 if (pos)
01508 {
01509
01510 return thumb_map[pos];
01511 } else
01512 {
01513 unfile_thumbnails();
01514 return DjVuDocument::get_thumbnail(page_num, dont_decode);
01515 }
01516 }
01517
01518 int
01519 DjVuDocEditor::get_thumbnails_num(void) const
01520 {
01521 GCriticalSectionLock lock((GCriticalSection *) &thumb_lock);
01522
01523 int cnt=0;
01524 int pages_num=get_pages_num();
01525 for(int page_num=0;page_num<pages_num;page_num++)
01526 {
01527 if (thumb_map.contains(page_to_id(page_num)))
01528 cnt++;
01529 }
01530 return cnt;
01531 }
01532
01533 int
01534 DjVuDocEditor::get_thumbnails_size(void) const
01535 {
01536 DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n");
01537 DEBUG_MAKE_INDENT(3);
01538
01539 GCriticalSectionLock lock((GCriticalSection *) &thumb_lock);
01540
01541 int pages_num=get_pages_num();
01542 for(int page_num=0;page_num<pages_num;page_num++)
01543 {
01544 const GPosition pos(thumb_map.contains(page_to_id(page_num)));
01545 if (pos)
01546 {
01547 const GP<ByteStream> gstr(thumb_map[pos]->get_stream());
01548 GP<IW44Image> iwpix=IW44Image::create_decode(IW44Image::COLOR);
01549 iwpix->decode_chunk(gstr);
01550
01551 int width=iwpix->get_width();
01552 int height=iwpix->get_height();
01553 return width<height ? width : height;
01554 }
01555 }
01556 return -1;
01557 }
01558
01559 void
01560 DjVuDocEditor::remove_thumbnails(void)
01561 {
01562 DEBUG_MSG("DjVuDocEditor::remove_thumbnails(): doing it\n");
01563 DEBUG_MAKE_INDENT(3);
01564
01565 unfile_thumbnails();
01566
01567 DEBUG_MSG("clearing thumb_map\n");
01568 GCriticalSectionLock lock(&thumb_lock);
01569 thumb_map.empty();
01570 }
01571
01572 void
01573 DjVuDocEditor::unfile_thumbnails(void)
01574
01575
01576
01577
01578
01579 {
01580 DEBUG_MSG("DjVuDocEditor::unfile_thumbnails(): updating DjVmDir\n");
01581 DEBUG_MAKE_INDENT(3);
01582
01583 {
01584 GCriticalSectionLock lock(&threqs_lock);
01585 threqs_list.empty();
01586 }
01587 if((const DjVmDir *)djvm_dir)
01588 {
01589 GPList<DjVmDir::File> xfiles_list=djvm_dir->get_files_list();
01590 for(GPosition pos=xfiles_list;pos;++pos)
01591 {
01592 GP<DjVmDir::File> f=xfiles_list[pos];
01593 if (f->is_thumbnails())
01594 djvm_dir->delete_file(f->get_load_name());
01595 }
01596 }
01597 }
01598
01599 void
01600 DjVuDocEditor::file_thumbnails(void)
01601
01602
01603
01604
01605 {
01606 DEBUG_MSG("DjVuDocEditor::file_thumbnails(): updating DjVmDir\n");
01607 DEBUG_MAKE_INDENT(3);
01608 unfile_thumbnails();
01609
01610
01611 int thumb_num=get_thumbnails_num();
01612 int size=thumb_num>0 ? get_thumbnails_size() : 128;
01613 if (thumb_num!=get_pages_num())
01614 {
01615 generate_thumbnails(size);
01616 }
01617
01618 DEBUG_MSG("filing thumbnails\n");
01619
01620 GCriticalSectionLock lock(&thumb_lock);
01621
01622
01623 int ipf=1;
01624 int image_num=0;
01625 int page_num=0, pages_num=djvm_dir->get_pages_num();
01626 GP<ByteStream> str(ByteStream::create());
01627 GP<IFFByteStream> iff(IFFByteStream::create(str));
01628 iff->put_chunk("FORM:THUM");
01629 for(;;)
01630 {
01631 GUTF8String id(page_to_id(page_num));
01632 const GPosition pos(thumb_map.contains(id));
01633 if (! pos)
01634 {
01635 G_THROW( ERR_MSG("DjVuDocEditor.no_thumb") "\t"+GUTF8String(page_num));
01636 }
01637 iff->put_chunk("TH44");
01638 iff->copy(*(thumb_map[pos]->get_stream()));
01639 iff->close_chunk();
01640 image_num++;
01641 page_num++;
01642 if (image_num>=ipf || page_num>=pages_num)
01643 {
01644 int i=id.rsearch('.');
01645 if(i<=0)
01646 {
01647 i=id.length();
01648 }
01649 id=id.substr(0,i)+".thumb";
01650
01651 id=find_unique_id(id);
01652
01653
01654 GP<DjVmDir::File> file(DjVmDir::File::create(id, id, id,
01655 DjVmDir::File::THUMBNAILS));
01656
01657
01658
01659 int file_pos=djvm_dir->get_page_pos(page_num-image_num);
01660 djvm_dir->insert_file(file, file_pos);
01661
01662
01663
01664
01665
01666
01667 iff->close_chunk();
01668 str->seek(0);
01669 const GP<DataPool> file_pool(DataPool::create(str));
01670 GP<File> f=new File;
01671 f->pool=file_pool;
01672 GCriticalSectionLock lock(&files_lock);
01673 files_map[id]=f;
01674
01675
01676 str=ByteStream::create();
01677 iff=IFFByteStream::create(str);
01678 iff->put_chunk("FORM:THUM");
01679 image_num=0;
01680
01681
01682
01683 if (page_num==1) ipf=thumbnails_per_file;
01684 if (page_num>=pages_num) break;
01685 }
01686 }
01687 }
01688
01689 int
01690 DjVuDocEditor::generate_thumbnails(int thumb_size, int page_num)
01691 {
01692 DEBUG_MSG("DjVuDocEditor::generate_thumbnails(): doing it\n");
01693 DEBUG_MAKE_INDENT(3);
01694
01695 if(page_num<(djvm_dir->get_pages_num()))
01696 {
01697 const GUTF8String id(page_to_id(page_num));
01698 if (!thumb_map.contains(id))
01699 {
01700 const GP<DjVuImage> dimg(get_page(page_num, true));
01701
01702 GRect rect(0, 0, thumb_size, dimg->get_height()*thumb_size/dimg->get_width());
01703 GP<GPixmap> pm=dimg->get_pixmap(rect, rect, get_thumbnails_gamma());
01704 if (!pm)
01705 {
01706 const GP<GBitmap> bm(dimg->get_bitmap(rect, rect, sizeof(int)));
01707 if (bm)
01708 pm = GPixmap::create(*bm);
01709 else
01710 pm = GPixmap::create(rect.height(), rect.width(), &GPixel::WHITE);
01711 }
01712
01713 const GP<IW44Image> iwpix(IW44Image::create_encode(*pm));
01714 const GP<ByteStream> gstr(ByteStream::create());
01715 IWEncoderParms parms;
01716 parms.slices=97;
01717 parms.bytes=0;
01718 parms.decibels=0;
01719 iwpix->encode_chunk(gstr, parms);
01720 gstr->seek(0L);
01721 thumb_map[id]=DataPool::create(gstr);
01722 }
01723 ++page_num;
01724 }
01725 else
01726 {
01727 page_num = -1;
01728 }
01729 return page_num;
01730 }
01731
01732 void
01733 DjVuDocEditor::generate_thumbnails(int thumb_size,
01734 bool (* cb)(int page_num, void *),
01735 void * cl_data)
01736 {
01737 int page_num=0;
01738 do
01739 {
01740 page_num=generate_thumbnails(thumb_size,page_num);
01741 if (cb) if (cb(page_num, cl_data)) return;
01742 } while(page_num>=0);
01743 }
01744
01745 static void
01746 store_file(const GP<DjVmDir> & src_djvm_dir, const GP<DjVmDoc> & djvm_doc,
01747 GP<DjVuFile> & djvu_file, GMap<GURL, void *> & map)
01748 {
01749 GURL url=djvu_file->get_url();
01750 if (!map.contains(url))
01751 {
01752 map[url]=0;
01753
01754
01755 GPList<DjVuFile> djvu_files_list=djvu_file->get_included_files(false);
01756 for(GPosition pos=djvu_files_list;pos;++pos)
01757 store_file(src_djvm_dir, djvm_doc, djvu_files_list[pos], map);
01758
01759
01760 GP<DataPool> file_data=djvu_file->get_djvu_data(false);
01761 GP<DjVmDir::File> frec=src_djvm_dir->name_to_file(url.name());
01762 if (frec)
01763 {
01764 frec=new DjVmDir::File(*frec);
01765 djvm_doc->insert_file(frec, file_data, -1);
01766 }
01767 }
01768 }
01769
01770 void
01771 DjVuDocEditor::save_pages_as(
01772 const GP<ByteStream> &str, const GList<int> & _page_list)
01773 {
01774 GList<int> page_list=sortList(_page_list);
01775
01776 GP<DjVmDoc> djvm_doc=DjVmDoc::create();
01777 GMap<GURL, void *> map;
01778 for(GPosition pos=page_list;pos;++pos)
01779 {
01780 GP<DjVmDir::File> frec=djvm_dir->page_to_file(page_list[pos]);
01781 if (frec)
01782 {
01783 GP<DjVuFile> djvu_file=get_djvu_file(frec->get_load_name());
01784 if (djvu_file)
01785 store_file(djvm_dir, djvm_doc, djvu_file, map);
01786 }
01787 }
01788 djvm_doc->write(str);
01789 }
01790
01791 void
01792 DjVuDocEditor::save_file(const GUTF8String &file_id, const GURL &codebase,
01793 const bool only_modified, GMap<GUTF8String,GUTF8String> & map)
01794 {
01795 if(only_modified)
01796 {
01797 for(GPosition pos=files_map;pos;++pos)
01798 {
01799 const GP<File> file_rec(files_map[pos]);
01800 const bool file_modified=file_rec->pool ||
01801 (file_rec->file && file_rec->file->is_modified());
01802 if(!file_modified)
01803 {
01804 const GUTF8String id=files_map.key(pos);
01805 const GUTF8String save_name(djvm_dir->id_to_file(id)->get_save_name());
01806 if(id == save_name)
01807 {
01808 map[id]=id;
01809 }
01810 }
01811 }
01812 }
01813 save_file(file_id,codebase,map);
01814 }
01815
01816 void
01817 DjVuDocEditor::save_file(
01818 const GUTF8String &file_id, const GURL &codebase,
01819 GMap<GUTF8String,GUTF8String> & map)
01820 {
01821 DEBUG_MSG("DjVuDocEditor::save_file(): ID='" << file_id << "'\n");
01822 DEBUG_MAKE_INDENT(3);
01823
01824 if (!map.contains(file_id))
01825 {
01826 const GP<DjVmDir::File> file(djvm_dir->id_to_file(file_id));
01827
01828 GP<DataPool> file_pool;
01829 const GPosition pos(files_map.contains(file_id));
01830 if (pos)
01831 {
01832 const GP<File> file_rec(files_map[pos]);
01833 if (file_rec->file)
01834 file_pool=file_rec->file->get_djvu_data(false);
01835 else
01836 file_pool=file_rec->pool;
01837 }
01838
01839 if (!file_pool)
01840 {
01841 DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
01842 file_pool=pcaster->request_data(this, id_to_url(file_id));
01843 }
01844
01845 if (file_pool)
01846 {
01847 GMap<GUTF8String,GUTF8String> incl;
01848 map[file_id]=get_djvm_doc()->save_file(codebase,*file,incl,file_pool);
01849 for(GPosition pos=incl;pos;++pos)
01850 {
01851 save_file(incl.key(pos),codebase ,map);
01852 }
01853 }else
01854 {
01855 map[file_id]=file->get_save_name();
01856 }
01857 }
01858 }
01859
01860 void
01861 DjVuDocEditor::save(void)
01862 {
01863 DEBUG_MSG("DjVuDocEditor::save(): saving the file\n");
01864 DEBUG_MAKE_INDENT(3);
01865
01866 if (!can_be_saved())
01867 G_THROW( ERR_MSG("DjVuDocEditor.cant_save") );
01868 save_as(GURL(), orig_doc_type!=INDIRECT);
01869 }
01870
01871 void
01872 DjVuDocEditor::write(const GP<ByteStream> &gbs, bool force_djvm)
01873 {
01874 DEBUG_MSG("DjVuDocEditor::write()\n");
01875 DEBUG_MAKE_INDENT(3);
01876 if (get_thumbnails_num()==get_pages_num())
01877 {
01878 file_thumbnails();
01879 }else
01880 {
01881 remove_thumbnails();
01882 }
01883 clean_files_map();
01884 DjVuDocument::write(gbs,force_djvm);
01885 }
01886
01887 void
01888 DjVuDocEditor::write(
01889 const GP<ByteStream> &gbs,const GMap<GUTF8String,void *> &reserved)
01890 {
01891 DEBUG_MSG("DjVuDocEditor::write()\n");
01892 DEBUG_MAKE_INDENT(3);
01893 if (get_thumbnails_num()==get_pages_num())
01894 {
01895 file_thumbnails();
01896 }else
01897 {
01898 remove_thumbnails();
01899 }
01900 clean_files_map();
01901 DjVuDocument::write(gbs,reserved);
01902 }
01903
01904 void
01905 DjVuDocEditor::save_as(const GURL &where, bool bundled)
01906 {
01907 DEBUG_MSG("DjVuDocEditor::save_as(): where='" << where << "'\n");
01908 DEBUG_MAKE_INDENT(3);
01909
01910
01911
01912
01913
01914
01915
01916
01917 if (get_thumbnails_num()==get_pages_num())
01918 {
01919 file_thumbnails();
01920 }else
01921 {
01922 remove_thumbnails();
01923 }
01924
01925 GURL save_doc_url;
01926
01927 if (where.is_empty())
01928 {
01929
01930
01931 bool can_be_saved_bundled=orig_doc_type==BUNDLED ||
01932 orig_doc_type==OLD_BUNDLED ||
01933 orig_doc_type==SINGLE_PAGE ||
01934 orig_doc_type==OLD_INDEXED && orig_doc_pages==1;
01935 if ((bundled ^ can_be_saved_bundled)!=0)
01936 G_THROW( ERR_MSG("DjVuDocEditor.cant_save2") );
01937 save_doc_url=doc_url;
01938 } else
01939 {
01940 save_doc_url=where;
01941 }
01942
01943 int save_doc_type=bundled ? BUNDLED : INDIRECT;
01944
01945 clean_files_map();
01946
01947 GCriticalSectionLock lock(&files_lock);
01948
01949 DjVuPortcaster * pcaster=DjVuPort::get_portcaster();
01950
01951
01952 if(needs_compression())
01953 {
01954 DEBUG_MSG("Compressing on output\n");
01955 remove_thumbnails();
01956 if(! djvu_compress_codec)
01957 {
01958 G_THROW( ERR_MSG("DjVuDocEditor.no_codec") );
01959 }
01960 const GP<DjVmDoc> doc(get_djvm_doc());
01961 GP<ByteStream> mbs(ByteStream::create());
01962 doc->write(mbs);
01963 mbs->flush();
01964 mbs->seek(0,SEEK_SET);
01965 djvu_compress_codec(mbs,save_doc_url,(!(const DjVmDir *)djvm_dir)||(djvm_dir->get_files_num()==1)||(save_doc_type!=INDIRECT));
01966 files_map.empty();
01967 doc_url=GURL();
01968 }else
01969 {
01970 if (djvm_dir->get_files_num()==1)
01971 {
01972
01973 DEBUG_MSG("saving one file...\n");
01974 GURL file_url=page_to_url(0);
01975 const GUTF8String file_id(djvm_dir->page_to_file(0)->get_load_name());
01976 GP<DataPool> file_pool;
01977 GPosition pos=files_map.contains(file_id);
01978 if (pos)
01979 {
01980 const GP<File> file_rec(files_map[pos]);
01981 if (file_rec->pool && (!file_rec->file ||
01982 !file_rec->file->is_modified()))
01983 {
01984 file_pool=file_rec->pool;
01985 }else if (file_rec->file)
01986 {
01987 file_pool=file_rec->file->get_djvu_data(false);
01988 }
01989 }
01990
01991
01992 if (!file_pool)
01993 file_pool=pcaster->request_data(this, file_url);
01994 if (file_pool)
01995 {
01996 DEBUG_MSG("Saving '" << file_url << "' to '" << save_doc_url << "'\n");
01997 DataPool::load_file(save_doc_url);
01998 const GP<ByteStream> gstr_out(ByteStream::create(save_doc_url, "wb"));
01999 ByteStream &str_out=*gstr_out;
02000 str_out.writall(octets, 4);
02001 const GP<ByteStream> str_in(file_pool->get_stream());
02002 str_out.copy(*str_in);
02003 }
02004
02005
02006 const GP<DjVmDoc> doc(get_djvm_doc());
02007 const GP<ByteStream> gstr=ByteStream::create();
02008 doc->write(gstr);
02009 gstr->seek(0, SEEK_SET);
02010 const GP<DataPool> pool(DataPool::create(gstr));
02011 doc_pool=pool;
02012 init_data_pool=pool;
02013
02014
02015 djvm_dir=doc->get_djvm_dir();
02016 } else if (save_doc_type==INDIRECT)
02017 {
02018 DEBUG_MSG("Saving in INDIRECT format to '" << save_doc_url << "'\n");
02019 bool save_only_modified=!(save_doc_url!=doc_url || save_doc_type!=orig_doc_type);
02020 GPList<DjVmDir::File> xfiles_list=djvm_dir->resolve_duplicates(false);
02021 const GURL codebase=save_doc_url.base();
02022 int pages_num=djvm_dir->get_pages_num();
02023 GMap<GUTF8String, GUTF8String> map;
02024
02025 for(int page_num=0;page_num<pages_num;page_num++)
02026 {
02027 const GUTF8String id(djvm_dir->page_to_file(page_num)->get_load_name());
02028 save_file(id, codebase, save_only_modified, map);
02029 }
02030
02031 GPosition pos;
02032 for(pos=xfiles_list;pos;++pos)
02033 save_file(xfiles_list[pos]->get_load_name(), codebase, save_only_modified, map);
02034
02035
02036 for(pos=xfiles_list;pos;++pos)
02037 {
02038 const GP<DjVmDir::File> file(xfiles_list[pos]);
02039 file->offset=0;
02040 file->size=0;
02041 }
02042 DataPool::load_file(save_doc_url);
02043 const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb"));
02044 const GP<IFFByteStream> giff(IFFByteStream::create(gstr));
02045 IFFByteStream &iff=*giff;
02046
02047 iff.put_chunk("FORM:DJVM", 1);
02048 iff.put_chunk("DIRM");
02049 djvm_dir->encode(giff->get_bytestream());
02050 iff.close_chunk();
02051 iff.close_chunk();
02052 iff.flush();
02053
02054
02055 doc_pool=DataPool::create(save_doc_url);
02056 init_data_pool=doc_pool;
02057
02058
02059
02060 } else if (save_doc_type==BUNDLED || save_doc_type==OLD_BUNDLED)
02061 {
02062 DEBUG_MSG("Saving in BUNDLED format to '" << save_doc_url << "'\n");
02063
02064
02065 const GP<DjVmDoc> doc(get_djvm_doc());
02066 DataPool::load_file(save_doc_url);
02067 const GP<ByteStream> gstr(ByteStream::create(save_doc_url, "wb"));
02068 doc->write(gstr);
02069 gstr->flush();
02070
02071
02072 doc_pool=DataPool::create(save_doc_url);
02073 init_data_pool=doc_pool;
02074
02075
02076 djvm_dir=doc->get_djvm_dir();
02077 } else
02078 {
02079 G_THROW( ERR_MSG("DjVuDocEditor.cant_save") );
02080 }
02081
02082
02083
02084
02085
02086
02087
02088
02089
02090
02091
02092
02093
02094 for(GPosition pos=files_map;pos;)
02095 {
02096 const GP<File> file_rec(files_map[pos]);
02097 file_rec->pool=0;
02098 if (file_rec->file==0)
02099 {
02100 GPosition this_pos=pos;
02101 ++pos;
02102 files_map.del(this_pos);
02103 } else
02104 {
02105
02106 if (doc_url!=save_doc_url ||
02107 orig_doc_type!=save_doc_type)
02108 if (save_doc_type==BUNDLED)
02109 file_rec->file->move(save_doc_url);
02110 else file_rec->file->move(save_doc_url.base());
02111 ++pos;
02112 }
02113 }
02114
02115 }
02116 orig_doc_type=save_doc_type;
02117 doc_type=save_doc_type;
02118
02119 if (doc_url!=save_doc_url)
02120 {
02121
02122 doc_url=save_doc_url;
02123 init_url=save_doc_url;
02124 }
02125 }
02126
02127 GP<DjVuDocEditor>
02128 DjVuDocEditor::create_wait(void)
02129 {
02130 DjVuDocEditor *doc=new DjVuDocEditor();
02131 const GP<DjVuDocEditor> retval(doc);
02132 doc->init();
02133 return retval;
02134 }
02135
02136 GP<DjVuDocEditor>
02137 DjVuDocEditor::create_wait(const GURL &url)
02138 {
02139 DjVuDocEditor *doc=new DjVuDocEditor();
02140 const GP<DjVuDocEditor> retval(doc);
02141 doc->init(url);
02142 return retval;
02143 }
02144
02145 bool
02146 DjVuDocEditor::inherits(const GUTF8String &class_name) const
02147 {
02148 return (class_name == "DjVuDocEditor")||DjVuDocument::inherits(class_name);
02149 }
02150
02151 int
02152 DjVuDocEditor::get_orig_doc_type(void) const
02153 {
02154 return orig_doc_type;
02155 }
02156
02157 bool
02158 DjVuDocEditor::can_be_saved(void) const
02159 {
02160 return !(needs_rename()||needs_compression()||orig_doc_type==UNKNOWN_TYPE ||
02161 orig_doc_type==OLD_INDEXED);
02162 }
02163
02164 int
02165 DjVuDocEditor::get_save_doc_type(void) const
02166 {
02167 if (orig_doc_type==SINGLE_PAGE)
02168 if (djvm_dir->get_files_num()==1)
02169 return SINGLE_PAGE;
02170 else
02171 return BUNDLED;
02172 else if (orig_doc_type==INDIRECT)
02173 return INDIRECT;
02174 else if (orig_doc_type==OLD_BUNDLED || orig_doc_type==BUNDLED)
02175 return BUNDLED;
02176 else
02177 return UNKNOWN_TYPE;
02178 }
02179
02180 GURL
02181 DjVuDocEditor::get_doc_url(void) const
02182 {
02183 return doc_url.is_empty() ? init_url : doc_url;
02184 }
02185
02186
02187
02188 #ifdef HAVE_NAMESPACES
02189 }
02190 # ifndef NOT_USING_DJVU_NAMESPACE
02191 using namespace DJVU;
02192 # endif
02193 #endif