00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <kmessagebox.h>
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 #include <qfileinfo.h>
00028 #include <qimage.h>
00029 #include <qpainter.h>
00030 #include <kapp.h>
00031
00032 #include "GBitmap.h"
00033 #include "BSByteStream.h"
00034 #include "IFFByteStream.h"
00035
00036 #include "prefs.h"
00037
00038 #include "documentWidget.h"
00039 #include "djvurenderer.h"
00040 #include "djvumultipage.h"
00041 #include "hyperlink.h"
00042 #include "renderedDocumentPagePixmap.h"
00043 #include "textBox.h"
00044
00045
00046
00047 inline GUTF8String GStringFromQString(const QString& x)
00048 {
00049 GUTF8String retval=(const char*)x.utf8();
00050 return retval;
00051 }
00052
00053
00054 inline QString QStringFromGString(const GUTF8String& x)
00055 {
00056 QString retval=QString::fromUtf8((const char*)x);
00057 return retval;
00058 }
00059
00060
00061 DjVuRenderer::DjVuRenderer(QWidget* par)
00062 : DocumentRenderer(par)
00063 {
00064 #ifdef KF_DEBUG
00065 kdError() << "DjVuRenderer( parent=" << par << " )" << endl;
00066 #endif
00067
00068 PPMstream = ByteStream::create();
00069 }
00070
00071
00072
00073 DjVuRenderer::~DjVuRenderer()
00074 {
00075 #ifdef KF_DEBUG
00076 kdDebug() << "~DjVuRenderer" << endl;
00077 #endif
00078
00079
00080 QMutexLocker locker( &mutex );
00081 }
00082
00083
00084 void DjVuRenderer::drawPage(double resolution, RenderedDocumentPage* page)
00085 {
00086 #ifdef KF_DEBUG
00087 kdDebug() << "DjVuRenderer::drawPage(documentPage*) called, page number " << page->getPageNumber() << endl;
00088 #endif
00089
00090
00091 if (page == 0) {
00092 kdError() << "DjVuRenderer::drawPage(documentPage*) called with argument == 0" << endl;
00093 return;
00094 }
00095 if (page->getPageNumber() == 0) {
00096 kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number 0" << endl;
00097 return;
00098 }
00099
00100
00101 QMutexLocker locker( &mutex );
00102
00103
00104 if (page->getPageNumber() > numPages) {
00105 kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number " << page->getPageNumber()
00106 << " but the current fax file has only " << numPages << " pages." << endl;
00107 return;
00108 }
00109
00110 int pageNumber = page->getPageNumber() - 1;
00111
00112 GP<DjVuImage> djvuPage = document->get_page(pageNumber, true);
00113 if (!djvuPage->wait_for_complete_decode())
00114 {
00115 kdDebug() << "decoding failed." << endl;
00116 return;
00117 }
00118
00119 if (!pageSizes[pageNumber].isValid())
00120 {
00121 int djvuResolution = djvuPage->get_dpi();
00122 int djvuPageWidth = djvuPage->get_width();
00123 int djvuPageHeight = djvuPage->get_height();
00124
00125 Length w, h;
00126 w.setLength_in_inch(djvuPageWidth / (double)djvuResolution);
00127 h.setLength_in_inch(djvuPageHeight / (double)djvuResolution);
00128 pageSizes[pageNumber].setPageSize(w, h);
00129
00130 SimplePageSize ps = sizeOfPage(page->getPageNumber());
00131
00132
00133 RenderedDocumentPagePixmap* pagePixmap = dynamic_cast<RenderedDocumentPagePixmap*>(page);
00134 if (pagePixmap)
00135 pagePixmap->resize(ps.sizeInPixel(resolution));
00136 }
00137
00138
00139
00140 int pageHeight = page->height();
00141 int pageWidth = page->width();
00142
00143 GRect pageRect(0, 0, pageWidth, pageHeight);
00144
00145
00146 GP<GPixmap> djvuPixmap;
00147 if (Prefs::renderMode() == Prefs::EnumRenderMode::Color)
00148 djvuPixmap = djvuPage->get_pixmap(pageRect, pageRect);
00149 else if (Prefs::renderMode() == Prefs::EnumRenderMode::Foreground)
00150 djvuPixmap = djvuPage->get_fg_pixmap(pageRect, pageRect);
00151 else if (Prefs::renderMode() == Prefs::EnumRenderMode::Background)
00152 djvuPixmap = djvuPage->get_bg_pixmap(pageRect, pageRect);
00153
00154 QPainter* foreGroundPaint = page->getPainter();
00155 if (foreGroundPaint != 0)
00156 {
00157 if(djvuPixmap && Prefs::renderMode() != Prefs::EnumRenderMode::BlackAndWhite)
00158 {
00159 PPMstream->seek(0);
00160 djvuPixmap->save_ppm(*PPMstream);
00161 long pixmapsize = PPMstream->tell();
00162 PPMstream->seek(0);
00163 uchar* buf = new uchar[pixmapsize];
00164 long bytesRead = PPMstream->readall(buf, pixmapsize);
00165
00166 bool ok = pixmap.loadFromData(buf, bytesRead, "PPM");
00167 if (!ok)
00168 {
00169 kdError() << "loading failed" << endl;
00170
00171 foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white);
00172 }
00173 foreGroundPaint->drawPixmap(0, 0, pixmap);
00174 delete[] buf;
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 }
00188 else
00189 {
00190 GP<GBitmap> djvuBitmap = djvuPage->get_bitmap(pageRect, pageRect);
00191 if(djvuBitmap)
00192 {
00193 PPMstream->seek(0);
00194 if(djvuBitmap->get_grays() == 2)
00195 djvuBitmap->save_pbm(*PPMstream);
00196 else
00197 djvuBitmap->save_pgm(*PPMstream);
00198
00199 long pixmapsize = PPMstream->tell();
00200 PPMstream->seek(0);
00201 uchar* buf = new uchar[pixmapsize];
00202 long bytesRead = PPMstream->readall(buf, pixmapsize);
00203
00204 bool ok = pixmap.loadFromData(buf, bytesRead, "PPM");
00205 if (!ok)
00206 {
00207 kdError() << "loading failed" << endl;
00208
00209 foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white);
00210 }
00211 foreGroundPaint->drawPixmap(0, 0, pixmap);
00212 delete[] buf;
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224 }
00225 else
00226 {
00227
00228 foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, Qt::white);
00229 }
00230 }
00231
00232
00233 page->returnPainter(foreGroundPaint);
00234 }
00235
00236 GP<DjVuTXT> pageText = getText(pageNumber);
00237
00238 if (pageText)
00239 {
00240 QSize djvuPageSize(djvuPage->get_width(), djvuPage->get_real_height());
00241 fillInText(page, pageText, pageText->page_zone, djvuPageSize);
00242
00243
00244 }
00245
00246 getAnnotations(page, djvuPage);
00247
00248 page->isEmpty = false;
00249 }
00250
00251
00252 bool DjVuRenderer::setFile(const QString &fname, const KURL &)
00253 {
00254 #ifdef KF_DEBUG
00255 kdDebug() << "DjVuRenderer::setFile(" << fname << ") called" << endl;
00256 #endif
00257
00258
00259 QMutexLocker locker( &mutex );
00260
00261
00262 if (fname.isEmpty()) {
00263 kdDebug() << "DjVuRenderer::setFile( ... ) called with empty filename. Closing the file." << endl;
00264 return true;
00265 }
00266
00267
00268
00269
00270 QFileInfo fi(fname);
00271 QString filename = fi.absFilePath();
00272 if (!fi.exists() || fi.isDir()) {
00273 KMessageBox::error( parentWidget,
00274 i18n("<qt><strong>File error.</strong> The specified file '%1' does not exist.</qt>").arg(filename),
00275 i18n("File Error"));
00276
00277 return false;
00278 }
00279
00280
00281 clear();
00282
00283
00284 G_TRY {
00285 document = DjVuDocEditor::create_wait(GURL::Filename::UTF8(GStringFromQString(filename)));
00286 }
00287 G_CATCH(ex) {
00288 ;
00289 }
00290 G_ENDCATCH;
00291
00292
00293 if (!document)
00294 {
00295 KMessageBox::error( parentWidget,
00296 i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").arg(filename),
00297 i18n("File Error"));
00298
00299 clear();
00300 kdDebug(1223) << "Loading of document failed." << endl;
00301 return false;
00302 }
00303
00304 bool r = initializeDocument();
00305
00306
00307 return r;
00308 }
00309
00310 void DjVuRenderer::getAnnotations(RenderedDocumentPage* page, GP<DjVuImage> djvuPage)
00311 {
00312 GP<ByteStream> annotations = djvuPage->get_anno();
00313 if (!(annotations && annotations->size()))
00314 return;
00315
00316 GP<DjVuANT> ant = DjVuANT::create();
00317
00318 GP<IFFByteStream> iff = IFFByteStream::create(annotations);
00319
00320 GUTF8String chkid;
00321
00322 while (iff->get_chunk(chkid))
00323 {
00324 if (chkid == "ANTa")
00325 {
00326 ant->merge(*iff->get_bytestream());
00327 }
00328 else if (chkid == "ANTz")
00329 {
00330 GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream());
00331 ant->merge(*bsiff);
00332 }
00333 iff->close_chunk();
00334 }
00335
00336 if (!ant->is_empty())
00337 {
00338
00339
00340 int pageWidth = page->width();
00341 int pageHeight = page->height();
00342
00343 double scaleX = pageWidth / (double)djvuPage->get_width();
00344 double scaleY = pageHeight / (double)djvuPage->get_height();
00345
00346 GPList<GMapArea> map = ant->map_areas;
00347
00348 for (GPosition pos = map; pos; ++pos)
00349 {
00350
00351 if (!map[pos]->get_shape_type() == GMapArea::RECT)
00352 continue;
00353
00354 GRect rect = map[pos]->get_bound_rect();
00355
00356 QRect hyperlinkRect((int)(rect.xmin*scaleX+0.5), (int)((djvuPage->get_height()-rect.ymax)*scaleY+0.5),
00357 (int)(rect.width()*scaleX +0.5), (int)(rect.height()*scaleY+0.5));
00358
00359 QString url((const char*)map[pos]->url);
00360 QString target((const char*)map[pos]->target);
00361 QString comment((const char*)map[pos]->comment);
00362
00363
00364 if (!anchorList.contains(url))
00365 {
00366
00367 if(url[0] == '#' && target == "_self")
00368 {
00369 bool conversionOk;
00370 PageNumber targetPage = url.remove('#').toInt(&conversionOk);
00371 if (conversionOk)
00372 anchorList[url] = Anchor(targetPage, Length());
00373 }
00374 }
00375
00376 Hyperlink hyperlink(hyperlinkRect.bottom(), hyperlinkRect, url);
00377 page->hyperLinkList.push_back(hyperlink);
00378 }
00379 }
00380 }
00381
00382
00383 bool DjVuRenderer::initializeDocument()
00384 {
00385 if (document == 0)
00386 return false;
00387
00388 if (!document->wait_for_complete_init()) {
00389 kdDebug() << "Document Initialization failed." << endl;
00390 return false;
00391 }
00392
00393
00394 numPages = document->get_pages_num();
00395
00396 pageSizes.resize(numPages);
00397 Length w,h;
00398
00399
00400
00401 if (numPages > 100)
00402 emit setStatusBarText(i18n("Loading file. Computing page sizes..."));
00403 for(Q_UINT16 i=0; i<numPages; i++) {
00404
00405 if (i%100 == 0)
00406 kapp->processEvents();
00407
00408 GP<DjVuFile> djvuFile = document->get_djvu_file(i);
00409 int resolution;
00410 int pageWidth;
00411 int pageHeight;
00412 bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution);
00413 if (!ok)
00414 kdError() << "Decoding info of page " << i << " failed." << endl;
00415 else {
00416 w.setLength_in_inch(pageWidth / (double)resolution);
00417 h.setLength_in_inch(pageHeight / (double)resolution);
00418 pageSizes[i].setPageSize(w, h);
00419 }
00420 }
00421 emit setStatusBarText(QString::null);
00422
00423
00424
00425 anchorList.clear();
00426 return true;
00427 }
00428
00429
00430 GP<DjVuTXT> DjVuRenderer::getText(PageNumber pageNumber)
00431 {
00432 GUTF8String chkid;
00433
00434 const GP<DjVuFile> file = document->get_djvu_file(pageNumber);
00435 const GP<ByteStream> bs(file->get_text());
00436 if (bs)
00437 {
00438 long int i=0;
00439 const GP<IFFByteStream> iff(IFFByteStream::create(bs));
00440 while (iff->get_chunk(chkid))
00441 {
00442 i++;
00443 if (chkid == GUTF8String("TXTa"))
00444 {
00445 GP<DjVuTXT> txt = DjVuTXT::create();
00446 txt->decode(iff->get_bytestream());
00447 return txt;
00448 }
00449 else if (chkid == GUTF8String("TXTz"))
00450 {
00451 GP<DjVuTXT> txt = DjVuTXT::create();
00452 GP<ByteStream> bsiff=BSByteStream::create(iff->get_bytestream());
00453 txt->decode(bsiff);
00454 return txt;
00455 }
00456 iff->close_chunk();
00457 }
00458 }
00459 return 0;
00460 }
00461
00462 void DjVuRenderer::fillInText(RenderedDocumentPage* page, const GP<DjVuTXT>& text, DjVuTXT::Zone& zone, QSize& djvuPageSize)
00463 {
00464 if (zone.children.isempty())
00465 {
00466 int pageWidth = page->width();
00467 int pageHeight = page->height();
00468
00469 double scaleX = pageWidth / (double)djvuPageSize.width();
00470 double scaleY = pageHeight / (double)djvuPageSize.height();
00471
00472 QString zoneString = QStringFromGString(text->textUTF8.substr(zone.text_start, zone.text_length));
00473
00474
00475
00476 QRect textRect((int)(zone.rect.xmin*scaleX+0.5), (int)((djvuPageSize.height()-zone.rect.ymax)*scaleY+0.5),
00477 (int)(zone.rect.width()*scaleX+0.5), (int)(zone.rect.height()*scaleY+0.5));
00478
00479 TextBox textBox(textRect, zoneString);
00480 page->textBoxList.push_back(textBox);
00481 }
00482 else
00483 {
00484 for (GPosition pos=zone.children; pos; ++pos)
00485 {
00486 fillInText(page, text, zone.children[pos], djvuPageSize);
00487 }
00488 }
00489 }
00490
00491 bool DjVuRenderer::getPageInfo(GP<DjVuFile> file, int& width, int& height, int& dpi)
00492 {
00493 if (!file || !file->is_all_data_present())
00494 return false;
00495
00496 const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false));
00497 const GP<IFFByteStream> iff(IFFByteStream::create(pbs));
00498
00499 GUTF8String chkid;
00500 if (iff->get_chunk(chkid))
00501 {
00502 if (chkid == "FORM:DJVU")
00503 {
00504 while (iff->get_chunk(chkid) && chkid!="INFO")
00505 iff->close_chunk();
00506 if (chkid == "INFO")
00507 {
00508 GP<ByteStream> gbs = iff->get_bytestream();
00509 GP<DjVuInfo> info=DjVuInfo::create();
00510 info->decode(*gbs);
00511 int rot = ((360-GRect::findangle(info->orientation))/90)%4;
00512
00513 width = (rot&1) ? info->height : info->width;
00514 height = (rot&1) ? info->width : info->height;
00515 dpi = info->dpi;
00516 return true;
00517 }
00518 }
00519 else if (chkid == "FORM:BM44" || chkid == "FORM:PM44")
00520 {
00521 while (iff->get_chunk(chkid) && chkid!="BM44" && chkid!="PM44")
00522 iff->close_chunk();
00523 if (chkid=="BM44" || chkid=="PM44")
00524 {
00525 GP<ByteStream> gbs = iff->get_bytestream();
00526 if (gbs->read8() == 0)
00527 {
00528 gbs->read8();
00529 gbs->read8();
00530 unsigned char xhi = gbs->read8();
00531 unsigned char xlo = gbs->read8();
00532 unsigned char yhi = gbs->read8();
00533 unsigned char ylo = gbs->read8();
00534
00535 width = (xhi<<8)+xlo;
00536 height = (yhi<<8)+ylo;
00537 dpi = 100;
00538 return true;
00539 }
00540 }
00541 }
00542 }
00543 return false;
00544 }
00545
00546 void DjVuRenderer::getText(RenderedDocumentPage* page)
00547 {
00548 QMutexLocker locker( &mutex );
00549
00550 int pageNumber = page->getPageNumber() - 1;
00551 GP<DjVuTXT> pageText = getText(pageNumber);
00552
00553 if (pageText)
00554 {
00555 GP<DjVuFile> djvuFile = document->get_djvu_file(pageNumber);
00556 int resolution;
00557 int pageWidth;
00558 int pageHeight;
00559 bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution);
00560
00561 if (ok)
00562 {
00563 QSize djvuPageSize(pageWidth, pageHeight);
00564 fillInText(page, pageText, pageText->page_zone, djvuPageSize);
00565 }
00566 }
00567 }
00568
00569
00570 bool DjVuRenderer::convertToPSFile( DjVuToPS &converter, QString filename, QValueList<int> &pageList )
00571 {
00572 if (document == 0) {
00573 kdError(1223) << "DjVuRenderer::convertToPSFile(..) called when document was 0" << endl;
00574 return false;
00575 }
00576
00577 QMutexLocker locker( &mutex );
00578
00579
00580 KProgressDialog *pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Printing..."), i18n("Preparing pages for printing..."), true);
00581 pdialog->setButtonText(i18n("Abort"));
00582 pdialog->showCancelButton(true);
00583 pdialog->progressBar()->setTotalSteps(pageList.size());
00584 pdialog->progressBar()->setFormat(QString::null);
00585
00586
00587 GURL outname = GURL::Filename::UTF8(GStringFromQString(filename));
00588 GP<ByteStream> obs = ByteStream::create(outname, "w");
00589
00590 QString pagename;
00591 QValueList<int>::ConstIterator it = pageList.begin();
00592 while (true) {
00593 pagename += QString::number(*it);
00594 ++it;
00595 if (it == pageList.end())
00596 break;
00597 pagename += ",";
00598 }
00599 GUTF8String pages = GStringFromQString(pagename);
00600
00601 converter.set_info_cb(printerInfoCallBack, (void*)pdialog);
00602 bool iscancelled = false;
00603 G_TRY {
00604 converter.print(*obs, (DjVuDocument *)document, pages );
00605 }
00606 G_CATCH(ex) {
00607 iscancelled = true;
00608 }
00609 G_ENDCATCH;
00610
00611 delete pdialog;
00612
00613
00614 kapp->processEvents();
00615
00616 obs->flush();
00617 return !iscancelled;
00618 }
00619
00620
00621 void DjVuRenderer::deletePages(Q_UINT16 from, Q_UINT16 to)
00622 {
00623
00624 if (document == 0) {
00625 kdError(1223) << "DjVuRenderer::deletePages(...) called when no document was loaded" << endl;
00626 return;
00627 }
00628 if ((from > to) || (from == 0) || (from > totalPages()) || (to > totalPages())) {
00629 kdError(1223) << "DjVuRenderer::deletePages(...) called with invalid arguments" << endl;
00630 return;
00631 }
00632
00633 QMutexLocker locker( &mutex );
00634
00635 KProgressDialog *pdialog = 0;
00636 if (to-from > 9) {
00637 pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Deleting pages..."), i18n("Please wait while pages are removed..."), true);
00638 pdialog->showCancelButton(false);
00639 pdialog->progressBar()->setTotalSteps(to-from+1);
00640 pdialog->progressBar()->setFormat(QString::null);
00641 pdialog->show();
00642 kapp->processEvents();
00643 }
00644
00645
00646
00647 GP<DjVuDocEditor> document_new = document;
00648 document = 0;
00649
00650
00651 if (pdialog == 0) {
00652 GList<int> pageList;
00653 for(Q_UINT16 i=from; i<= to; i++)
00654 pageList.append(i-1);
00655 document_new->remove_pages(pageList);
00656 } else {
00657 for(Q_UINT16 i=from; i<=to; i++) {
00658 document_new->remove_page(from-1);
00659 pdialog->progressBar()->setProgress(i-from);
00660 pdialog->progressBar()->setFormat(i18n("deleting page %1").arg(i));
00661 kapp->processEvents();
00662 }
00663 delete pdialog;
00664 }
00665 _isModified = true;
00666 document = document_new;
00667
00668 initializeDocument();
00669 }
00670
00671
00672 bool DjVuRenderer::save(const QString &filename)
00673 {
00674 if (document == 0) {
00675 kdError() << "DjVuRenderer::save(..) called when document==0" << endl;
00676 return false;
00677 }
00678
00679 QMutexLocker locker( &mutex );
00680
00681 G_TRY {
00682 document->save_as(GURL::Filename::UTF8(GStringFromQString(filename)), true);
00683 }
00684 G_CATCH(ex) {
00685 return false;
00686 }
00687 G_ENDCATCH;
00688
00689 document->save_as(GURL::Filename::UTF8(filename.ascii()), true);
00690
00691 if (QFile::exists(filename) == false)
00692 return false;
00693
00694 _isModified = false;
00695 return true;
00696 }
00697
00698
00699 void DjVuRenderer::printerInfoCallBack(int page_num, int page_count, int, DjVuToPS::Stage, void *pd)
00700 {
00701 if (pd == 0)
00702 return;
00703
00704
00705 KProgressDialog *pdialog = (KProgressDialog *)pd;
00706
00707 pdialog->progressBar()->setProgress(page_count);
00708 pdialog->progressBar()->setFormat(i18n("processing page %1").arg(page_num+1));
00709 pdialog->show();
00710
00711 if (pdialog->wasCancelled())
00712 G_THROW("STOP");
00713
00714
00715 kapp->processEvents();
00716 }
00717
00718
00719 #include "djvurenderer.moc"