|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2005 by Stefan Kebekus *
|
|
|
|
* kebekus@kde.org *
|
|
|
|
* *
|
|
|
|
* Copyright (C) 2005 by Wilfried Huss *
|
|
|
|
* Wilfried.Huss@gmx.at *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the *
|
|
|
|
* Free Software Foundation, Inc., *
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <kapp.h>
|
|
|
|
|
|
|
|
#include "GBitmap.h"
|
|
|
|
#include "BSByteStream.h"
|
|
|
|
#include "IFFByteStream.h"
|
|
|
|
|
|
|
|
#include "prefs.h"
|
|
|
|
|
|
|
|
#include "documentWidget.h"
|
|
|
|
#include "djvurenderer.h"
|
|
|
|
#include "djvumultipage.h"
|
|
|
|
#include "hyperlink.h"
|
|
|
|
#include "renderedDocumentPagePixmap.h"
|
|
|
|
#include "textBox.h"
|
|
|
|
|
|
|
|
//#define KF_DEBUG
|
|
|
|
|
|
|
|
inline GUTF8String GStringFromTQString(const TQString& x)
|
|
|
|
{
|
|
|
|
GUTF8String retval=(const char*)x.utf8();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline TQString TQStringFromGString(const GUTF8String& x)
|
|
|
|
{
|
|
|
|
TQString retval=TQString::fromUtf8((const char*)x);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DjVuRenderer::DjVuRenderer(TQWidget* par)
|
|
|
|
: DocumentRenderer(par)
|
|
|
|
{
|
|
|
|
#ifdef KF_DEBUG
|
|
|
|
kdError() << "DjVuRenderer( tqparent=" << par << " )" << endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
PPMstream = ByteStream::create();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DjVuRenderer::~DjVuRenderer()
|
|
|
|
{
|
|
|
|
#ifdef KF_DEBUG
|
|
|
|
kdDebug() << "~DjVuRenderer" << endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Wait for all access to this documentRenderer to finish
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DjVuRenderer::drawPage(double resolution, RenderedDocumentPage* page)
|
|
|
|
{
|
|
|
|
#ifdef KF_DEBUG
|
|
|
|
kdDebug() << "DjVuRenderer::drawPage(documentPage*) called, page number " << page->getPageNumber() << endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Paranoid safety checks
|
|
|
|
if (page == 0) {
|
|
|
|
kdError() << "DjVuRenderer::drawPage(documentPage*) called with argument == 0" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (page->getPageNumber() == 0) {
|
|
|
|
kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number 0" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all access to this documentRenderer to finish
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
// more paranoid safety checks
|
|
|
|
if (page->getPageNumber() > numPages) {
|
|
|
|
kdError() << "DjVuRenderer::drawPage(documentPage*) called for a documentPage with page number " << page->getPageNumber()
|
|
|
|
<< " but the current fax file has only " << numPages << " pages." << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pageNumber = page->getPageNumber() - 1;
|
|
|
|
|
|
|
|
GP<DjVuImage> djvuPage = document->get_page(pageNumber, true);
|
|
|
|
if (!djvuPage->wait_for_complete_decode())
|
|
|
|
{
|
|
|
|
kdDebug() << "decoding failed." << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pageSizes[pageNumber].isValid())
|
|
|
|
{
|
|
|
|
int djvuResolution = djvuPage->get_dpi();
|
|
|
|
int djvuPageWidth = djvuPage->get_width();
|
|
|
|
int djvuPageHeight = djvuPage->get_height();
|
|
|
|
|
|
|
|
Length w, h;
|
|
|
|
w.setLength_in_inch(djvuPageWidth / (double)djvuResolution);
|
|
|
|
h.setLength_in_inch(djvuPageHeight / (double)djvuResolution);
|
|
|
|
pageSizes[pageNumber].setPageSize(w, h);
|
|
|
|
|
|
|
|
SimplePageSize ps = sizeOfPage(page->getPageNumber());
|
|
|
|
|
|
|
|
// If we are not printing we need to resize the pixmap.
|
|
|
|
RenderedDocumentPagePixmap* pagePixmap = dynamic_cast<RenderedDocumentPagePixmap*>(page);
|
|
|
|
if (pagePixmap)
|
|
|
|
pagePixmap->resize(ps.sizeInPixel(resolution));
|
|
|
|
}
|
|
|
|
|
|
|
|
//kdDebug() << "render page " << pageNumber + 1 << " at size (" << pageWidth << ", " << pageHeight << ")" << endl;
|
|
|
|
|
|
|
|
int pageHeight = page->height();
|
|
|
|
int pageWidth = page->width();
|
|
|
|
|
|
|
|
GRect pageRect(0, 0, pageWidth, pageHeight);
|
|
|
|
|
|
|
|
|
|
|
|
GP<GPixmap> djvuPixmap;
|
|
|
|
if (Prefs::renderMode() == Prefs::EnumRenderMode::Color)
|
|
|
|
djvuPixmap = djvuPage->get_pixmap(pageRect, pageRect);
|
|
|
|
else if (Prefs::renderMode() == Prefs::EnumRenderMode::Foreground)
|
|
|
|
djvuPixmap = djvuPage->get_fg_pixmap(pageRect, pageRect);
|
|
|
|
else if (Prefs::renderMode() == Prefs::EnumRenderMode::Background)
|
|
|
|
djvuPixmap = djvuPage->get_bg_pixmap(pageRect, pageRect);
|
|
|
|
|
|
|
|
TQPainter* foreGroundPaint = page->getPainter();
|
|
|
|
if (foreGroundPaint != 0)
|
|
|
|
{
|
|
|
|
if(djvuPixmap && Prefs::renderMode() != Prefs::EnumRenderMode::BlackAndWhite)
|
|
|
|
{
|
|
|
|
PPMstream->seek(0);
|
|
|
|
djvuPixmap->save_ppm(*PPMstream);
|
|
|
|
long pixmapsize = PPMstream->tell();
|
|
|
|
PPMstream->seek(0);
|
|
|
|
uchar* buf = new uchar[pixmapsize];
|
|
|
|
long bytesRead = PPMstream->readall(buf, pixmapsize);
|
|
|
|
|
|
|
|
bool ok = pixmap.loadFromData(buf, bytesRead, "PPM");
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
kdError() << "loading failed" << endl;
|
|
|
|
//draw an empty page
|
|
|
|
foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, TQt::white);
|
|
|
|
}
|
|
|
|
foreGroundPaint->drawPixmap(0, 0, pixmap);
|
|
|
|
delete[] buf;
|
|
|
|
|
|
|
|
/* for (int i = 0; i < pageHeight; i++)
|
|
|
|
{
|
|
|
|
GPixel* pixmapRow = (*djvuPixmap)[i];
|
|
|
|
|
|
|
|
for (int j = 0; j < pageWidth; j++)
|
|
|
|
{
|
|
|
|
GPixel pixel = pixmapRow[j];
|
|
|
|
foreGroundPaint->setPen(TQColor(pixel.r, pixel.g, pixel.b));
|
|
|
|
foreGroundPaint->drawPoint(j, pageHeight - i - 1);
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GP<GBitmap> djvuBitmap = djvuPage->get_bitmap(pageRect, pageRect);
|
|
|
|
if(djvuBitmap)
|
|
|
|
{
|
|
|
|
PPMstream->seek(0);
|
|
|
|
if(djvuBitmap->get_grays() == 2)
|
|
|
|
djvuBitmap->save_pbm(*PPMstream);
|
|
|
|
else
|
|
|
|
djvuBitmap->save_pgm(*PPMstream);
|
|
|
|
|
|
|
|
long pixmapsize = PPMstream->tell();
|
|
|
|
PPMstream->seek(0);
|
|
|
|
uchar* buf = new uchar[pixmapsize];
|
|
|
|
long bytesRead = PPMstream->readall(buf, pixmapsize);
|
|
|
|
|
|
|
|
bool ok = pixmap.loadFromData(buf, bytesRead, "PPM");
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
kdError() << "loading failed" << endl;
|
|
|
|
//draw an empty page
|
|
|
|
foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, TQt::white);
|
|
|
|
}
|
|
|
|
foreGroundPaint->drawPixmap(0, 0, pixmap);
|
|
|
|
delete[] buf;
|
|
|
|
/*
|
|
|
|
for (int i = 0; i < pageHeight; i++)
|
|
|
|
{
|
|
|
|
unsigned char* bitmapRow = (*djvuBitmap)[i];
|
|
|
|
for (int j = 0; j < pageWidth; j++)
|
|
|
|
{
|
|
|
|
unsigned char pixel = 255-bitmapRow[j];
|
|
|
|
foreGroundPaint->setPen(TQColor(pixel, pixel, pixel));
|
|
|
|
foreGroundPaint->drawPoint(j, pageHeight - i - 1);
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//draw an empty page
|
|
|
|
foreGroundPaint->fillRect(0, 0, pageWidth, pageHeight, TQt::white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//kdDebug() << "rendering page " << pageNumber + 1 << " at size (" << pageWidth << ", " << pageHeight << ") finished." << endl;
|
|
|
|
page->returnPainter(foreGroundPaint);
|
|
|
|
}
|
|
|
|
|
|
|
|
GP<DjVuTXT> pageText = getText(pageNumber);
|
|
|
|
|
|
|
|
if (pageText)
|
|
|
|
{
|
|
|
|
TQSize djvuPageSize(djvuPage->get_width(), djvuPage->get_real_height());
|
|
|
|
fillInText(page, pageText, pageText->page_zone, djvuPageSize);
|
|
|
|
//kdDebug() << "Text of page " << pageNumber << endl;
|
|
|
|
//kdDebug() << (const char*)pageText->textUTF8 << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
getAnnotations(page, djvuPage);
|
|
|
|
|
|
|
|
page->isEmpty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DjVuRenderer::setFile(const TQString &fname, const KURL &)
|
|
|
|
{
|
|
|
|
#ifdef KF_DEBUG
|
|
|
|
kdDebug() << "DjVuRenderer::setFile(" << fname << ") called" << endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Wait for all access to this documentRenderer to finish
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
// If fname is the empty string, then this means: "close".
|
|
|
|
if (fname.isEmpty()) {
|
|
|
|
kdDebug() << "DjVuRenderer::setFile( ... ) called with empty filename. Closing the file." << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paranoid saftey checks: make sure the file actually exists, and
|
|
|
|
// that it is a file, not a directory. Otherwise, show an error
|
|
|
|
// message and exit..
|
|
|
|
TQFileInfo fi(fname);
|
|
|
|
TQString filename = fi.absFilePath();
|
|
|
|
if (!fi.exists() || fi.isDir()) {
|
|
|
|
KMessageBox::error( parentWidget,
|
|
|
|
i18n("<qt><strong>File error.</strong> The specified file '%1' does not exist.</qt>").tqarg(filename),
|
|
|
|
i18n("File Error"));
|
|
|
|
// the return value 'false' indicates that this operation was not successful.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear previously loaded document
|
|
|
|
clear();
|
|
|
|
|
|
|
|
// Now we assume that the file is fine and load the file.
|
|
|
|
G_TRY {
|
|
|
|
document = DjVuDocEditor::create_wait(GURL::Filename::UTF8(GStringFromTQString(filename)));
|
|
|
|
}
|
|
|
|
G_CATCH(ex) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
G_ENDCATCH;
|
|
|
|
|
|
|
|
// If the above assumption was false.
|
|
|
|
if (!document)
|
|
|
|
{
|
|
|
|
KMessageBox::error( parentWidget,
|
|
|
|
i18n("<qt><strong>File error.</strong> The specified file '%1' could not be loaded.</qt>").tqarg(filename),
|
|
|
|
i18n("File Error"));
|
|
|
|
|
|
|
|
clear();
|
|
|
|
kdDebug(1223) << "Loading of document failed." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool r = initializeDocument();
|
|
|
|
|
|
|
|
// the return value 'true' indicates that this operation was successful.
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DjVuRenderer::getAnnotations(RenderedDocumentPage* page, GP<DjVuImage> djvuPage)
|
|
|
|
{
|
|
|
|
GP<ByteStream> annotations = djvuPage->get_anno();
|
|
|
|
if (!(annotations && annotations->size()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
GP<DjVuANT> ant = DjVuANT::create();
|
|
|
|
|
|
|
|
GP<IFFByteStream> iff = IFFByteStream::create(annotations);
|
|
|
|
|
|
|
|
GUTF8String chkid;
|
|
|
|
|
|
|
|
while (iff->get_chunk(chkid))
|
|
|
|
{
|
|
|
|
if (chkid == "ANTa")
|
|
|
|
{
|
|
|
|
ant->merge(*iff->get_bytestream());
|
|
|
|
}
|
|
|
|
else if (chkid == "ANTz")
|
|
|
|
{
|
|
|
|
GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream());
|
|
|
|
ant->merge(*bsiff);
|
|
|
|
}
|
|
|
|
iff->close_chunk();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ant->is_empty())
|
|
|
|
{
|
|
|
|
// Scaling factors for the coordinate conversion.
|
|
|
|
// TODO: Refractor this into a function shared with fillInText.
|
|
|
|
int pageWidth = page->width();
|
|
|
|
int pageHeight = page->height();
|
|
|
|
|
|
|
|
double scaleX = pageWidth / (double)djvuPage->get_width();
|
|
|
|
double scaleY = pageHeight / (double)djvuPage->get_height();
|
|
|
|
|
|
|
|
GPList<GMapArea> map = ant->map_areas;
|
|
|
|
|
|
|
|
for (GPosition pos = map; pos; ++pos)
|
|
|
|
{
|
|
|
|
// Currently we only support rectangular links
|
|
|
|
if (!map[pos]->get_tqshape_type() == GMapArea::RECT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
GRect rect = map[pos]->get_bound_rect();
|
|
|
|
|
|
|
|
TQRect hyperlinkRect((int)(rect.xmin*scaleX+0.5), (int)((djvuPage->get_height()-rect.ymax)*scaleY+0.5),
|
|
|
|
(int)(rect.width()*scaleX +0.5), (int)(rect.height()*scaleY+0.5));
|
|
|
|
|
|
|
|
TQString url((const char*)map[pos]->url);
|
|
|
|
TQString target((const char*)map[pos]->target);
|
|
|
|
TQString comment((const char*)map[pos]->comment);
|
|
|
|
|
|
|
|
// Create an anchor for this link.
|
|
|
|
if (!anchorList.tqcontains(url))
|
|
|
|
{
|
|
|
|
// For now we only accept links to pages in the same document.
|
|
|
|
if(url[0] == '#' && target == "_self")
|
|
|
|
{
|
|
|
|
bool conversionOk;
|
|
|
|
PageNumber targetPage = url.remove('#').toInt(&conversionOk);
|
|
|
|
if (conversionOk)
|
|
|
|
anchorList[url] = Anchor(targetPage, Length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Hyperlink hyperlink(hyperlinkRect.bottom(), hyperlinkRect, url);
|
|
|
|
page->hyperLinkList.push_back(hyperlink);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DjVuRenderer::initializeDocument()
|
|
|
|
{
|
|
|
|
if (document == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!document->wait_for_complete_init()) {
|
|
|
|
kdDebug() << "Document Initialization failed." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the number of pages page sizes
|
|
|
|
numPages = document->get_pages_num();
|
|
|
|
|
|
|
|
pageSizes.resize(numPages);
|
|
|
|
Length w,h;
|
|
|
|
|
|
|
|
// Set the page sizes in the pageSizes array. Give feedback for
|
|
|
|
// very long documents
|
|
|
|
if (numPages > 100)
|
|
|
|
emit setStatusBarText(i18n("Loading file. Computing page sizes..."));
|
|
|
|
for(TQ_UINT16 i=0; i<numPages; i++) {
|
|
|
|
// Keep the GUI updated
|
|
|
|
if (i%100 == 0)
|
|
|
|
kapp->processEvents();
|
|
|
|
|
|
|
|
GP<DjVuFile> djvuFile = document->get_djvu_file(i);
|
|
|
|
int resolution;
|
|
|
|
int pageWidth;
|
|
|
|
int pageHeight;
|
|
|
|
bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution);
|
|
|
|
if (!ok)
|
|
|
|
kdError() << "Decoding info of page " << i << " failed." << endl;
|
|
|
|
else {
|
|
|
|
w.setLength_in_inch(pageWidth / (double)resolution);
|
|
|
|
h.setLength_in_inch(pageHeight / (double)resolution);
|
|
|
|
pageSizes[i].setPageSize(w, h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit setStatusBarText(TQString());
|
|
|
|
|
|
|
|
// We will also generate a list of hyperlink-anchors in the document.
|
|
|
|
// So declare the existing lists empty.
|
|
|
|
anchorList.clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GP<DjVuTXT> DjVuRenderer::getText(PageNumber pageNumber)
|
|
|
|
{
|
|
|
|
GUTF8String chkid;
|
|
|
|
|
|
|
|
const GP<DjVuFile> file = document->get_djvu_file(pageNumber);
|
|
|
|
const GP<ByteStream> bs(file->get_text());
|
|
|
|
if (bs)
|
|
|
|
{
|
|
|
|
long int i=0;
|
|
|
|
const GP<IFFByteStream> iff(IFFByteStream::create(bs));
|
|
|
|
while (iff->get_chunk(chkid))
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
if (chkid == GUTF8String("TXTa"))
|
|
|
|
{
|
|
|
|
GP<DjVuTXT> txt = DjVuTXT::create();
|
|
|
|
txt->decode(iff->get_bytestream());
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
else if (chkid == GUTF8String("TXTz"))
|
|
|
|
{
|
|
|
|
GP<DjVuTXT> txt = DjVuTXT::create();
|
|
|
|
GP<ByteStream> bsiff=BSByteStream::create(iff->get_bytestream());
|
|
|
|
txt->decode(bsiff);
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
iff->close_chunk();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DjVuRenderer::fillInText(RenderedDocumentPage* page, const GP<DjVuTXT>& text, DjVuTXT::Zone& zone, TQSize& djvuPageSize)
|
|
|
|
{
|
|
|
|
if (zone.tqchildren.isempty())
|
|
|
|
{
|
|
|
|
int pageWidth = page->width();
|
|
|
|
int pageHeight = page->height();
|
|
|
|
|
|
|
|
double scaleX = pageWidth / (double)djvuPageSize.width();
|
|
|
|
double scaleY = pageHeight / (double)djvuPageSize.height();
|
|
|
|
|
|
|
|
TQString zoneString = TQStringFromGString(text->textUTF8.substr(zone.text_start, zone.text_length));
|
|
|
|
|
|
|
|
//kdDebug() << "zone text: " << zoneString << endl;
|
|
|
|
|
|
|
|
TQRect textRect((int)(zone.rect.xmin*scaleX+0.5), (int)((djvuPageSize.height()-zone.rect.ymax)*scaleY+0.5),
|
|
|
|
(int)(zone.rect.width()*scaleX+0.5), (int)(zone.rect.height()*scaleY+0.5));
|
|
|
|
//kdDebug() << "zone rect: " << textRect.x() << ", " << textRect.y() << ", " << textRect.width() << ", " << textRect.height() << endl;
|
|
|
|
TextBox textBox(textRect, zoneString);
|
|
|
|
page->textBoxList.push_back(textBox);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (GPosition pos=zone.tqchildren; pos; ++pos)
|
|
|
|
{
|
|
|
|
fillInText(page, text, zone.tqchildren[pos], djvuPageSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DjVuRenderer::getPageInfo(GP<DjVuFile> file, int& width, int& height, int& dpi)
|
|
|
|
{
|
|
|
|
if (!file || !file->is_all_data_present())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false));
|
|
|
|
const GP<IFFByteStream> iff(IFFByteStream::create(pbs));
|
|
|
|
|
|
|
|
GUTF8String chkid;
|
|
|
|
if (iff->get_chunk(chkid))
|
|
|
|
{
|
|
|
|
if (chkid == "FORM:DJVU")
|
|
|
|
{
|
|
|
|
while (iff->get_chunk(chkid) && chkid!="INFO")
|
|
|
|
iff->close_chunk();
|
|
|
|
if (chkid == "INFO")
|
|
|
|
{
|
|
|
|
GP<ByteStream> gbs = iff->get_bytestream();
|
|
|
|
GP<DjVuInfo> info=DjVuInfo::create();
|
|
|
|
info->decode(*gbs);
|
|
|
|
int rot = ((360-GRect::findangle(info->orientation))/90)%4;
|
|
|
|
|
|
|
|
width = (rot&1) ? info->height : info->width;
|
|
|
|
height = (rot&1) ? info->width : info->height;
|
|
|
|
dpi = info->dpi;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (chkid == "FORM:BM44" || chkid == "FORM:PM44")
|
|
|
|
{
|
|
|
|
while (iff->get_chunk(chkid) && chkid!="BM44" && chkid!="PM44")
|
|
|
|
iff->close_chunk();
|
|
|
|
if (chkid=="BM44" || chkid=="PM44")
|
|
|
|
{
|
|
|
|
GP<ByteStream> gbs = iff->get_bytestream();
|
|
|
|
if (gbs->read8() == 0)
|
|
|
|
{
|
|
|
|
gbs->read8();
|
|
|
|
gbs->read8();
|
|
|
|
unsigned char xhi = gbs->read8();
|
|
|
|
unsigned char xlo = gbs->read8();
|
|
|
|
unsigned char yhi = gbs->read8();
|
|
|
|
unsigned char ylo = gbs->read8();
|
|
|
|
|
|
|
|
width = (xhi<<8)+xlo;
|
|
|
|
height = (yhi<<8)+ylo;
|
|
|
|
dpi = 100;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DjVuRenderer::getText(RenderedDocumentPage* page)
|
|
|
|
{
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
int pageNumber = page->getPageNumber() - 1;
|
|
|
|
GP<DjVuTXT> pageText = getText(pageNumber);
|
|
|
|
|
|
|
|
if (pageText)
|
|
|
|
{
|
|
|
|
GP<DjVuFile> djvuFile = document->get_djvu_file(pageNumber);
|
|
|
|
int resolution;
|
|
|
|
int pageWidth;
|
|
|
|
int pageHeight;
|
|
|
|
bool ok = getPageInfo(djvuFile, pageWidth, pageHeight, resolution);
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
{
|
|
|
|
TQSize djvuPageSize(pageWidth, pageHeight);
|
|
|
|
fillInText(page, pageText, pageText->page_zone, djvuPageSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DjVuRenderer::convertToPSFile( DjVuToPS &converter, TQString filename, TQValueList<int> &pageList )
|
|
|
|
{
|
|
|
|
if (document == 0) {
|
|
|
|
kdError(1223) << "DjVuRenderer::convertToPSFile(..) called when document was 0" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
// Set up progress dialog
|
|
|
|
KProgressDialog *pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Printing..."), i18n("Preparing pages for printing..."), true);
|
|
|
|
pdialog->setButtonText(i18n("Abort"));
|
|
|
|
pdialog->showCancelButton(true);
|
|
|
|
pdialog->progressBar()->setTotalSteps(pageList.size());
|
|
|
|
pdialog->progressBar()->setFormat(TQString());
|
|
|
|
|
|
|
|
// Open output file
|
|
|
|
GURL outname = GURL::Filename::UTF8(GStringFromTQString(filename));
|
|
|
|
GP<ByteStream> obs = ByteStream::create(outname, "w");
|
|
|
|
|
|
|
|
TQString pagename;
|
|
|
|
TQValueList<int>::ConstIterator it = pageList.begin();
|
|
|
|
while (true) {
|
|
|
|
pagename += TQString::number(*it);
|
|
|
|
++it;
|
|
|
|
if (it == pageList.end())
|
|
|
|
break;
|
|
|
|
pagename += ",";
|
|
|
|
}
|
|
|
|
GUTF8String pages = GStringFromTQString(pagename);
|
|
|
|
|
|
|
|
converter.set_info_cb(printerInfoCallBack, (void*)pdialog);
|
|
|
|
bool iscancelled = false;
|
|
|
|
G_TRY {
|
|
|
|
converter.print(*obs, (DjVuDocument *)document, pages );
|
|
|
|
}
|
|
|
|
G_CATCH(ex) {
|
|
|
|
iscancelled = true;
|
|
|
|
}
|
|
|
|
G_ENDCATCH;
|
|
|
|
|
|
|
|
delete pdialog;
|
|
|
|
|
|
|
|
// This is to keep the GUI updated.
|
|
|
|
kapp->processEvents();
|
|
|
|
|
|
|
|
obs->flush();
|
|
|
|
return !iscancelled;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DjVuRenderer::deletePages(TQ_UINT16 from, TQ_UINT16 to)
|
|
|
|
{
|
|
|
|
// Paranoia security checks
|
|
|
|
if (document == 0) {
|
|
|
|
kdError(1223) << "DjVuRenderer::deletePages(...) called when no document was loaded" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((from > to) || (from == 0) || (from > totalPages()) || (to > totalPages())) {
|
|
|
|
kdError(1223) << "DjVuRenderer::deletePages(...) called with invalid arguments" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
KProgressDialog *pdialog = 0;
|
|
|
|
if (to-from > 9) {
|
|
|
|
pdialog = new KProgressDialog(parentWidget, "Printing-ProgressDialog", i18n("Deleting pages..."), i18n("Please wait while pages are removed..."), true);
|
|
|
|
pdialog->showCancelButton(false);
|
|
|
|
pdialog->progressBar()->setTotalSteps(to-from+1);
|
|
|
|
pdialog->progressBar()->setFormat(TQString());
|
|
|
|
pdialog->show();
|
|
|
|
kapp->processEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the document pointer temporarily to 0, so that no-one tries
|
|
|
|
// to render a page while we are deleting pages
|
|
|
|
GP<DjVuDocEditor> document_new = document;
|
|
|
|
document = 0;
|
|
|
|
|
|
|
|
// Delete pages
|
|
|
|
if (pdialog == 0) {
|
|
|
|
GList<int> pageList;
|
|
|
|
for(TQ_UINT16 i=from; i<= to; i++)
|
|
|
|
pageList.append(i-1);
|
|
|
|
document_new->remove_pages(pageList);
|
|
|
|
} else {
|
|
|
|
for(TQ_UINT16 i=from; i<=to; i++) {
|
|
|
|
document_new->remove_page(from-1);
|
|
|
|
pdialog->progressBar()->setProgress(i-from);
|
|
|
|
pdialog->progressBar()->setFormat(i18n("deleting page %1").tqarg(i));
|
|
|
|
kapp->processEvents();
|
|
|
|
}
|
|
|
|
delete pdialog;
|
|
|
|
}
|
|
|
|
_isModified = true;
|
|
|
|
document = document_new;
|
|
|
|
|
|
|
|
initializeDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DjVuRenderer::save(const TQString &filename)
|
|
|
|
{
|
|
|
|
if (document == 0) {
|
|
|
|
kdError() << "DjVuRenderer::save(..) called when document==0" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQMutexLocker locker( &mutex );
|
|
|
|
|
|
|
|
G_TRY {
|
|
|
|
document->save_as(GURL::Filename::UTF8(GStringFromTQString(filename)), true);
|
|
|
|
}
|
|
|
|
G_CATCH(ex) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
G_ENDCATCH;
|
|
|
|
|
|
|
|
document->save_as(GURL::Filename::UTF8(filename.ascii()), true);
|
|
|
|
|
|
|
|
if (TQFile::exists(filename) == false)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
_isModified = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DjVuRenderer::printerInfoCallBack(int page_num, int page_count, int, DjVuToPS::Stage, void *pd)
|
|
|
|
{
|
|
|
|
if (pd == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Update the progress dialog.
|
|
|
|
KProgressDialog *pdialog = (KProgressDialog *)pd;
|
|
|
|
|
|
|
|
pdialog->progressBar()->setProgress(page_count);
|
|
|
|
pdialog->progressBar()->setFormat(i18n("processing page %1").tqarg(page_num+1));
|
|
|
|
pdialog->show();
|
|
|
|
|
|
|
|
if (pdialog->wasCancelled())
|
|
|
|
G_THROW("STOP");
|
|
|
|
|
|
|
|
// This is to keep the GUI updated.
|
|
|
|
kapp->processEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "djvurenderer.moc"
|