mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-05 23:49:20 +00:00
A new backend for oKular: DjVu:
* uses the DjVuLibre for loading the DjVu files * can rotate the pages * has a cache (with few elements) with the most recently used generated pixmaps, to reduce a bit the number of pixmap generation There could be some issues, but mainly it works nicely. svn path=/branches/work/kde4/playground/graphics/cmake/; revision=549184
This commit is contained in:
parent
d8b4dc78ad
commit
d0ac95dafd
41
cmake/modules/modules/FindDjVuLibre.cmake
Normal file
41
cmake/modules/modules/FindDjVuLibre.cmake
Normal file
|
@ -0,0 +1,41 @@
|
|||
# - Try to find the DjVuLibre library
|
||||
# Once done this will define
|
||||
#
|
||||
# DJVULIBRE_FOUND - system has the DjVuLibre library
|
||||
# DJVULIBRE_INCLUDE_DIR - the DjVuLibre include directory
|
||||
# DJVULIBRE_LIBRARY - Link this to use the DjVuLibre library
|
||||
#
|
||||
include(CheckLibraryExists)
|
||||
|
||||
# reset vars
|
||||
set(DJVULIBRE_INCLUDE_DIR)
|
||||
set(DJVULIBRE_LIBRARY)
|
||||
|
||||
find_path(DJVULIBRE_INCLUDE_DIR libdjvu/ddjvuapi.h
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
${GNUWIN32_DIR}/include
|
||||
)
|
||||
|
||||
find_library(DJVULIBRE_LIBRARY NAMES djvulibre
|
||||
PATHS
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
${GNUWIN32_DIR}/lib
|
||||
)
|
||||
|
||||
if(DJVULIBRE_INCLUDE_DIR AND DJVULIBRE_LIBRARY)
|
||||
set(DJVULIBRE_FOUND TRUE)
|
||||
endif(DJVULIBRE_INCLUDE_DIR AND DJVULIBRE_LIBRARY)
|
||||
|
||||
if (DJVULIBRE_FOUND)
|
||||
if (NOT DJVULIBRE_FIND_QUIETLY)
|
||||
message(STATUS "Found DjVuLibre: ${DJVULIBRE_LIBRARY}")
|
||||
endif (NOT DJVULIBRE_FIND_QUIETLY)
|
||||
else (DJVULIBRE_FOUND)
|
||||
if (DJVULIBRE_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could NOT find DjVuLibre")
|
||||
endif (DJVULIBRE_FIND_REQUIRED)
|
||||
endif (DJVULIBRE_FOUND)
|
||||
|
||||
mark_as_advanced(DJVULIBRE_INCLUDE_DIR DJVULIBRE_LIBRARY)
|
|
@ -11,6 +11,9 @@ add_subdirectory( kimgio )
|
|||
if(CHM_FOUND)
|
||||
add_subdirectory( chm )
|
||||
endif(CHM_FOUND)
|
||||
if(DJVULIBRE_FOUND)
|
||||
add_subdirectory(djvu)
|
||||
endif(DJVULIBRE_FOUND)
|
||||
|
||||
#original Makefile.am contents follow:
|
||||
|
||||
|
|
30
generators/djvu/CMakeLists.txt
Normal file
30
generators/djvu/CMakeLists.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
include_directories(
|
||||
${DJVULIBRE_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/okular
|
||||
${CMAKE_BINARY_DIR}/okular
|
||||
)
|
||||
|
||||
|
||||
########### next target ###############
|
||||
|
||||
set(okularGenerator_djvu_SRCS
|
||||
generator_djvu.cpp
|
||||
kdjvu.cpp
|
||||
)
|
||||
|
||||
kde4_automoc(${okularGenerator_djvu_SRCS})
|
||||
|
||||
kde4_add_plugin(okularGenerator_djvu WITH_PREFIX ${okularGenerator_djvu_SRCS})
|
||||
|
||||
kde4_install_libtool_file( ${PLUGIN_INSTALL_DIR} okularGenerator_djvu )
|
||||
|
||||
target_link_libraries(okularGenerator_djvu ${KDE4_KDECORE_LIBS} oKularcore ${DJVULIBRE_LIBRARY} ${KDE4_KDEPRINT_LIBS} ${KDE4_KDEUI_LIBS} )
|
||||
|
||||
install_targets(${PLUGIN_INSTALL_DIR} okularGenerator_djvu )
|
||||
|
||||
|
||||
########### install files ###############
|
||||
|
||||
install_files( ${SERVICES_INSTALL_DIR} FILES libokularGenerator_djvu.desktop )
|
||||
|
||||
|
124
generators/djvu/generator_djvu.cpp
Normal file
124
generators/djvu/generator_djvu.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Pino Toscano <toscano.pino@tiscali.it> *
|
||||
* *
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "generator_djvu.h"
|
||||
#include "kdjvu.h"
|
||||
#include "core/page.h"
|
||||
|
||||
#include <qstring.h>
|
||||
#include <kdebug.h>
|
||||
#include <klocale.h>
|
||||
|
||||
KPDF_EXPORT_PLUGIN(DjVuGenerator)
|
||||
|
||||
DjVuGenerator::DjVuGenerator( KPDFDocument * doc ) : Generator ( doc ),
|
||||
m_docInfo( 0 ), ready( false )
|
||||
{
|
||||
m_djvu = new KDjVu();
|
||||
connect( m_djvu, SIGNAL( pixmapGenerated( int, const QPixmap & ) ), this, SLOT( djvuPixmapGenerated( int, const QPixmap & ) ) );
|
||||
}
|
||||
|
||||
bool DjVuGenerator::loadDocument( const QString & fileName, QVector< KPDFPage * > & pagesVector )
|
||||
{
|
||||
delete m_docInfo;
|
||||
m_docInfo = 0;
|
||||
|
||||
if ( !m_djvu->openFile( fileName ) )
|
||||
return false;
|
||||
|
||||
const QVector<KDjVu::Page*> &djvu_pages = m_djvu->pages();
|
||||
int numofpages = djvu_pages.count();
|
||||
pagesVector.resize( numofpages );
|
||||
|
||||
for ( int i = 0; i < numofpages; ++i )
|
||||
{
|
||||
const KDjVu::Page *p = djvu_pages.at( i );
|
||||
KPDFPage *page = new KPDFPage( i, p->width(), p->height(), p->orientation() );
|
||||
pagesVector[i] = page;
|
||||
}
|
||||
|
||||
ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DjVuGenerator::canGeneratePixmap( bool /*async*/ )
|
||||
{
|
||||
return ready;
|
||||
}
|
||||
|
||||
void DjVuGenerator::generatePixmap( PixmapRequest * request )
|
||||
{
|
||||
ready = false;
|
||||
|
||||
m_request = request;
|
||||
|
||||
QPixmap pix = m_djvu->pixmap( request->pageNumber, request->width, request->height, request->documentRotation );
|
||||
if ( pix.isNull() )
|
||||
{
|
||||
|
||||
m_djvu->requestPixmap( request->pageNumber, request->width, request->height, request->documentRotation );
|
||||
}
|
||||
else
|
||||
{
|
||||
djvuPixmapGenerated( request->pageNumber, pix );
|
||||
}
|
||||
}
|
||||
|
||||
const DocumentInfo * DjVuGenerator::generateDocumentInfo()
|
||||
{
|
||||
if ( m_docInfo )
|
||||
return m_docInfo;
|
||||
|
||||
m_docInfo = new DocumentInfo();
|
||||
|
||||
m_docInfo->set( "mimeType", "image/x-djvu" );
|
||||
|
||||
if ( m_djvu )
|
||||
{
|
||||
// compile internal structure reading properties from KDjVu
|
||||
QString doctype = m_djvu->getMetaData( "documentType" );
|
||||
m_docInfo->set( "documentType", doctype.isEmpty() ? i18n( "Unknown" ) : doctype, i18n( "Type of document" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_docInfo->set( "documentType", i18n( "Unknown" ), i18n( "Type of document" ) );
|
||||
}
|
||||
|
||||
return m_docInfo;
|
||||
}
|
||||
|
||||
void DjVuGenerator::setOrientation( QVector<KPDFPage*> & pagesVector, int orientation )
|
||||
{
|
||||
const QVector<KDjVu::Page*> &djvu_pages = m_djvu->pages();
|
||||
int numofpages = djvu_pages.count();
|
||||
pagesVector.resize( numofpages );
|
||||
|
||||
for ( int i = 0; i < numofpages; ++i )
|
||||
{
|
||||
const KDjVu::Page *p = djvu_pages.at( i );
|
||||
delete pagesVector[i];
|
||||
int w = p->width();
|
||||
int h = p->height();
|
||||
if ( orientation % 2 == 1 )
|
||||
qSwap( w, h );
|
||||
KPDFPage *page = new KPDFPage( i, w, h, orientation );
|
||||
pagesVector[i] = page;
|
||||
}
|
||||
}
|
||||
|
||||
void DjVuGenerator::djvuPixmapGenerated( int /*page*/, const QPixmap & pix )
|
||||
{
|
||||
m_request->page->setPixmap( m_request->id, new QPixmap( pix ) );
|
||||
|
||||
ready = true;
|
||||
signalRequestDone( m_request );
|
||||
}
|
||||
|
||||
|
||||
#include "generator_djvu.moc"
|
53
generators/djvu/generator_djvu.h
Normal file
53
generators/djvu/generator_djvu.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Pino Toscano <toscano.pino@tiscali.it> *
|
||||
* *
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _DJVU_GENERATOR_H_
|
||||
#define _DJVU_GENERATOR_H_
|
||||
|
||||
#include "core/generator.h"
|
||||
#include <qvector.h>
|
||||
|
||||
class KDjVu;
|
||||
|
||||
class DjVuGenerator : public Generator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DjVuGenerator( KPDFDocument * doc );
|
||||
bool loadDocument( const QString & fileName, QVector<KPDFPage*> & pagesVector );
|
||||
|
||||
// pixmap generation
|
||||
bool canGeneratePixmap( bool async );
|
||||
void generatePixmap( PixmapRequest * request );
|
||||
|
||||
// document information
|
||||
const DocumentInfo * generateDocumentInfo();
|
||||
|
||||
// rotation handling
|
||||
bool supportsRotation() { return true; };
|
||||
void setOrientation( QVector<KPDFPage*> & pagesVector, int orientation );
|
||||
|
||||
signals:
|
||||
void error(QString & string, int duration);
|
||||
void warning(QString & string, int duration);
|
||||
void notice(QString & string, int duration);
|
||||
|
||||
private slots:
|
||||
void djvuPixmapGenerated( int page, const QPixmap & pix );
|
||||
|
||||
private:
|
||||
KDjVu *m_djvu;
|
||||
|
||||
DocumentInfo *m_docInfo;
|
||||
bool ready;
|
||||
|
||||
PixmapRequest *m_request;
|
||||
};
|
||||
|
||||
#endif
|
445
generators/djvu/kdjvu.cpp
Normal file
445
generators/djvu/kdjvu.cpp
Normal file
|
@ -0,0 +1,445 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Pino Toscano <toscano.pino@tiscali.it> *
|
||||
* *
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "kdjvu.h"
|
||||
|
||||
#include <qlist.h>
|
||||
#include <qmap.h>
|
||||
#include <qpainter.h>
|
||||
#include <qstring.h>
|
||||
#include <kdebug.h>
|
||||
#include <klocale.h>
|
||||
|
||||
#include <libdjvu/ddjvuapi.h>
|
||||
|
||||
kdbgstream &operator<<( kdbgstream & s, const ddjvu_rect_t &r )
|
||||
{
|
||||
s << "[" << r.x << "," << r.y << " - " << r.w << "x" << r.h << "]";
|
||||
return s;
|
||||
}
|
||||
|
||||
static void which_ddjvu_message( const ddjvu_message_t *msg )
|
||||
{
|
||||
kDebug() << "which_djvu_message(...): " << msg->m_any.tag << endl;
|
||||
switch( msg->m_any.tag )
|
||||
{
|
||||
case DDJVU_ERROR:
|
||||
kDebug() << "ERROR: file " << msg->m_error.filename << ", line " << msg->m_error.lineno << endl;
|
||||
kDebug() << "ERROR: function '" << msg->m_error.function << "'" << endl;
|
||||
kDebug() << "ERROR: '" << msg->m_error.message << "'" << endl;
|
||||
break;
|
||||
case DDJVU_INFO:
|
||||
kDebug() << "INFO: '" << msg->m_info.message << "'" << endl;
|
||||
break;
|
||||
case DDJVU_CHUNK:
|
||||
kDebug() << "CHUNK: '" << QByteArray( msg->m_chunk.chunkid ) << "'" << endl;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explore the message queue until there are message left in it.
|
||||
*/
|
||||
static void handle_ddjvu_messages( ddjvu_context_t *ctx, int wait )
|
||||
{
|
||||
const ddjvu_message_t *msg;
|
||||
if ( wait )
|
||||
ddjvu_message_wait( ctx );
|
||||
while ( ( msg = ddjvu_message_peek( ctx ) ) )
|
||||
{
|
||||
which_ddjvu_message( msg );
|
||||
ddjvu_message_pop( ctx );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explore the message queue until the message \p mid is found.
|
||||
*/
|
||||
static void wait_for_ddjvu_message( ddjvu_context_t *ctx, ddjvu_message_tag_t mid )
|
||||
{
|
||||
ddjvu_message_wait( ctx );
|
||||
const ddjvu_message_t *msg;
|
||||
while ( ( msg = ddjvu_message_peek( ctx ) ) && msg && ( msg->m_any.tag != mid ) )
|
||||
{
|
||||
which_ddjvu_message( msg );
|
||||
ddjvu_message_pop( ctx );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a clockwise coefficient \p r for a rotation to a counter-clockwise
|
||||
* and vice versa.
|
||||
*/
|
||||
static int flipRotation( int r )
|
||||
{
|
||||
return ( 4 - r ) % 4;
|
||||
}
|
||||
|
||||
|
||||
// PixmapCacheItem
|
||||
|
||||
class PixmapCacheItem
|
||||
{
|
||||
public:
|
||||
PixmapCacheItem( int p, int w, int h, int r, const QPixmap& px )
|
||||
: page( p ), width( w ), height( h ), rotation( r ), pix( px ) { }
|
||||
|
||||
int page;
|
||||
int width;
|
||||
int height;
|
||||
int rotation;
|
||||
QPixmap pix;
|
||||
};
|
||||
|
||||
|
||||
// KdjVu::Page
|
||||
|
||||
KDjVu::Page::Page()
|
||||
{
|
||||
}
|
||||
|
||||
KDjVu::Page::~Page()
|
||||
{
|
||||
}
|
||||
|
||||
int KDjVu::Page::width() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
int KDjVu::Page::height() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
int KDjVu::Page::dpi() const
|
||||
{
|
||||
return m_dpi;
|
||||
}
|
||||
|
||||
int KDjVu::Page::orientation() const
|
||||
{
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
class KDjVu::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: m_djvu_cxt( 0 ), m_djvu_document( 0 ), m_format( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
QPixmap generatePixmapTile( ddjvu_page_t *djvupage, int& res,
|
||||
int width, int row, int xdelta, int height, int col, int ydelta );
|
||||
|
||||
ddjvu_context_t *m_djvu_cxt;
|
||||
ddjvu_document_t *m_djvu_document;
|
||||
ddjvu_format_t *m_format;
|
||||
|
||||
QVector<KDjVu::Page*> m_pages;
|
||||
QVector<ddjvu_page_t *> m_pages_cache;
|
||||
|
||||
QList<PixmapCacheItem*> mPixCache;
|
||||
|
||||
QMap<QString, QString> m_metaData;
|
||||
};
|
||||
|
||||
QPixmap KDjVu::Private::generatePixmapTile( ddjvu_page_t *djvupage, int& res,
|
||||
int width, int row, int xdelta, int height, int col, int ydelta )
|
||||
{
|
||||
ddjvu_rect_t renderrect;
|
||||
renderrect.x = row * xdelta;
|
||||
renderrect.y = col * ydelta;
|
||||
int realwidth = qMin( width - renderrect.x, xdelta );
|
||||
int realheight = qMin( height - renderrect.y, ydelta );
|
||||
renderrect.w = realwidth;
|
||||
renderrect.h = realheight;
|
||||
kDebug() << "renderrect: " << renderrect << endl;
|
||||
ddjvu_rect_t pagerect;
|
||||
pagerect.x = 0;
|
||||
pagerect.y = 0;
|
||||
pagerect.w = width;
|
||||
pagerect.h = height;
|
||||
kDebug() << "pagerect: " << pagerect << endl;
|
||||
handle_ddjvu_messages( m_djvu_cxt, false );
|
||||
char* imagebuffer = new char[ realwidth * realheight * 4 + 1 ];
|
||||
res = ddjvu_page_render( djvupage, DDJVU_RENDER_COLOR,
|
||||
&pagerect, &renderrect, m_format, realwidth * 4, imagebuffer );
|
||||
kDebug() << "rendering result: " << res << endl;
|
||||
handle_ddjvu_messages( m_djvu_cxt, false );
|
||||
QPixmap pix;
|
||||
if ( res )
|
||||
{
|
||||
QImage img( (uchar*)imagebuffer, realwidth, realheight, QImage::Format_RGB32 );
|
||||
pix = QPixmap::fromImage( img );
|
||||
}
|
||||
delete [] imagebuffer;
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
|
||||
KDjVu::KDjVu() : QObject(), d( new Private )
|
||||
{
|
||||
// creating the djvu context
|
||||
d->m_djvu_cxt = ddjvu_context_create( "KDjVu" );
|
||||
// creating the rendering format
|
||||
unsigned int* mask = new unsigned int[3];
|
||||
mask[0] = 0x00ff0000;
|
||||
mask[1] = 0x0000ff00;
|
||||
mask[2] = 0x000000ff;
|
||||
d->m_format = ddjvu_format_create( DDJVU_FORMAT_RGBMASK32, 3, mask );
|
||||
ddjvu_format_set_row_order( d->m_format, 1 );
|
||||
ddjvu_format_set_y_direction( d->m_format, 1 );
|
||||
}
|
||||
|
||||
|
||||
KDjVu::~KDjVu()
|
||||
{
|
||||
closeFile();
|
||||
|
||||
ddjvu_format_release( d->m_format );
|
||||
ddjvu_context_release( d->m_djvu_cxt );
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KDjVu::openFile( const QString & fileName )
|
||||
{
|
||||
// first, close the old file
|
||||
if ( d->m_djvu_document )
|
||||
closeFile();
|
||||
|
||||
// load the document...
|
||||
d->m_djvu_document = ddjvu_document_create_by_filename( d->m_djvu_cxt, qPrintable( fileName ), true );
|
||||
if ( !d->m_djvu_document ) return false;
|
||||
// ...and wait for its loading
|
||||
wait_for_ddjvu_message( d->m_djvu_cxt, DDJVU_DOCINFO );
|
||||
|
||||
kDebug() << "# of pages: " << ddjvu_document_get_pagenum( d->m_djvu_document ) << endl;
|
||||
int numofpages = ddjvu_document_get_pagenum( d->m_djvu_document );
|
||||
d->m_pages.clear();
|
||||
d->m_pages.resize( numofpages );
|
||||
d->m_pages_cache.clear();
|
||||
d->m_pages_cache.resize( numofpages );
|
||||
|
||||
// get the document type
|
||||
QString doctype;
|
||||
switch ( ddjvu_document_get_type( d->m_djvu_document ) )
|
||||
{
|
||||
case DDJVU_DOCTYPE_UNKNOWN:
|
||||
doctype = i18nc( "Type of DjVu document", "Unknown" );
|
||||
break;
|
||||
case DDJVU_DOCTYPE_SINGLEPAGE:
|
||||
doctype = i18nc( "Type of DjVu document", "Single Page" );
|
||||
break;
|
||||
case DDJVU_DOCTYPE_BUNDLED:
|
||||
doctype = i18nc( "Type of DjVu document", "Bundled" );
|
||||
break;
|
||||
case DDJVU_DOCTYPE_INDIRECT:
|
||||
doctype = i18nc( "Type of DjVu document", "Indirect" );
|
||||
break;
|
||||
case DDJVU_DOCTYPE_OLD_BUNDLED:
|
||||
doctype = i18nc( "Type of DjVu document", "Bundled (old)" );
|
||||
break;
|
||||
case DDJVU_DOCTYPE_OLD_INDEXED:
|
||||
doctype = i18nc( "Type of DjVu document", "Indexed (old)" );
|
||||
break;
|
||||
}
|
||||
if ( !doctype.isEmpty() )
|
||||
d->m_metaData[ "documentType" ] = doctype;
|
||||
|
||||
// read the pages
|
||||
for ( int i = 0; i < numofpages; ++i )
|
||||
{
|
||||
// wait for the new page to be loaded
|
||||
ddjvu_page_t *newpage = ddjvu_page_create_by_pageno( d->m_djvu_document, i );
|
||||
ddjvu_status_t sts;
|
||||
while ( ( sts = ddjvu_page_decoding_status( newpage ) ) < DDJVU_JOB_OK )
|
||||
handle_ddjvu_messages( d->m_djvu_cxt, true );
|
||||
if ( sts >= DDJVU_JOB_FAILED )
|
||||
{
|
||||
kDebug() << "\t>>> page " << i << " failed: " << sts << endl;
|
||||
break;
|
||||
}
|
||||
d->m_pages_cache[i] = newpage;
|
||||
|
||||
KDjVu::Page *p = new KDjVu::Page();
|
||||
p->m_width = ddjvu_page_get_width( newpage );
|
||||
p->m_height = ddjvu_page_get_height( newpage );
|
||||
p->m_dpi = ddjvu_page_get_resolution( newpage );
|
||||
p->m_orientation = flipRotation( ddjvu_page_get_initial_rotation( newpage ) );
|
||||
d->m_pages[i] = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KDjVu::closeFile()
|
||||
{
|
||||
// deleting the pages
|
||||
qDeleteAll( d->m_pages );
|
||||
// releasing the djvu pages
|
||||
QVector<ddjvu_page_t *>::Iterator it = d->m_pages_cache.begin(), itEnd = d->m_pages_cache.end();
|
||||
for ( ; it != itEnd; ++it )
|
||||
ddjvu_page_release( *it );
|
||||
d->m_pages_cache.clear();
|
||||
// clearing the pixmap cache
|
||||
qDeleteAll( d->mPixCache );
|
||||
// clearing the old metadata
|
||||
d->m_metaData.clear();
|
||||
// releasing the old document
|
||||
if ( d->m_djvu_document )
|
||||
ddjvu_document_release( d->m_djvu_document );
|
||||
d->m_djvu_document = 0;
|
||||
}
|
||||
|
||||
QString KDjVu::getMetaData( const QString & key ) const
|
||||
{
|
||||
return d->m_metaData.contains( key ) ? d->m_metaData[ key ] : QString();
|
||||
}
|
||||
|
||||
const QVector<KDjVu::Page*> &KDjVu::pages() const
|
||||
{
|
||||
return d->m_pages;
|
||||
}
|
||||
|
||||
QPixmap KDjVu::pixmap( int page, int width, int height, int rotation )
|
||||
{
|
||||
bool found = false;
|
||||
QList<PixmapCacheItem*>::Iterator it = d->mPixCache.begin(), itEnd = d->mPixCache.end();
|
||||
for ( ; ( it != itEnd ) && !found; ++it )
|
||||
{
|
||||
PixmapCacheItem* cur = *it;
|
||||
if ( ( cur->page == page ) &&
|
||||
( cur->width == width ) &&
|
||||
( cur->height == height ) &&
|
||||
( cur->rotation == rotation ) )
|
||||
found = true;
|
||||
}
|
||||
if ( !found )
|
||||
return QPixmap();
|
||||
|
||||
// taking the element and pushing to the top of the list
|
||||
--it;
|
||||
PixmapCacheItem* cur2 = *it;
|
||||
d->mPixCache.erase( it );
|
||||
d->mPixCache.push_front( cur2 );
|
||||
|
||||
return cur2->pix;
|
||||
}
|
||||
|
||||
void KDjVu::requestPixmap( int page, int width, int height, int rotation )
|
||||
{
|
||||
QPixmap tmp = pixmap( page, width, height, rotation );
|
||||
if ( !tmp.isNull() )
|
||||
{
|
||||
emit pixmapGenerated( page, tmp );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !d->m_pages_cache.at( page ) )
|
||||
{
|
||||
ddjvu_page_t *newpage = ddjvu_page_create_by_pageno( d->m_djvu_document, page );
|
||||
// wait for the new page to be loaded
|
||||
ddjvu_status_t sts;
|
||||
while ( ( sts = ddjvu_page_decoding_status( newpage ) ) < DDJVU_JOB_OK )
|
||||
handle_ddjvu_messages( d->m_djvu_cxt, true );
|
||||
d->m_pages_cache[page] = newpage;
|
||||
}
|
||||
ddjvu_page_t *djvupage = d->m_pages_cache[page];
|
||||
|
||||
if ( ddjvu_page_get_rotation( djvupage ) != flipRotation( rotation ) )
|
||||
{
|
||||
// TODO: test documents with initial rotation != 0
|
||||
// ddjvu_page_set_rotation( djvupage, m_pages.at( page )->orientation() );
|
||||
ddjvu_page_set_rotation( djvupage, (ddjvu_page_rotation_t)flipRotation( rotation ) );
|
||||
}
|
||||
|
||||
static const int xdelta = 1500;
|
||||
static const int ydelta = 1500;
|
||||
|
||||
int xparts = width / xdelta + 1;
|
||||
int yparts = height / ydelta + 1;
|
||||
|
||||
QPixmap newpix( width, height );
|
||||
|
||||
int res = 10000;
|
||||
if ( ( xparts == 1 ) && ( yparts == 1 ) )
|
||||
{
|
||||
// only one part -- render at once with no need to auxiliary pixmap
|
||||
newpix = d->generatePixmapTile( djvupage, res,
|
||||
width, 0, xdelta, height, 0, ydelta );
|
||||
}
|
||||
else
|
||||
{
|
||||
// more than one part -- need to render piece-by-piece and to compose
|
||||
// the results
|
||||
QPainter p;
|
||||
p.begin( &newpix );
|
||||
int parts = xparts * yparts;
|
||||
for ( int i = 0; i < parts; ++i )
|
||||
{
|
||||
int row = i % xparts;
|
||||
int col = i / xparts;
|
||||
int tmpres = 0;
|
||||
QPixmap tempp = d->generatePixmapTile( djvupage, tmpres,
|
||||
width, row, xdelta, height, col, ydelta );
|
||||
if ( tmpres )
|
||||
{
|
||||
p.drawPixmap( row * xdelta, col * ydelta, tempp );
|
||||
}
|
||||
res = qMin( tmpres, res );
|
||||
}
|
||||
p.end();
|
||||
}
|
||||
|
||||
QPixmap pix;
|
||||
|
||||
if ( res )
|
||||
{
|
||||
pix = newpix;
|
||||
|
||||
// delete all the cached pixmaps for the current page with a size that
|
||||
// differs no more than 35% of the new pixmap size
|
||||
int pixsize = pix.width() * pix.height();
|
||||
if ( pixsize > 0 )
|
||||
{
|
||||
for( int i = 0; i < d->mPixCache.count(); )
|
||||
{
|
||||
PixmapCacheItem* cur = d->mPixCache.at(i);
|
||||
if ( ( cur->page == page ) &&
|
||||
( cur->rotation == rotation ) &&
|
||||
( abs( cur->pix.width() * cur->pix.height() - pixsize ) < pixsize * 0.35 ) )
|
||||
{
|
||||
d->mPixCache.removeAt( i );
|
||||
delete cur;
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// the pixmap cache has too many elements, remove the last
|
||||
if ( d->mPixCache.size() >= 10 )
|
||||
{
|
||||
delete d->mPixCache.last();
|
||||
d->mPixCache.removeLast();
|
||||
}
|
||||
PixmapCacheItem* pch = new PixmapCacheItem( page, width, height, rotation, pix );
|
||||
d->mPixCache.push_front( pch );
|
||||
}
|
||||
|
||||
emit pixmapGenerated( page, pix );
|
||||
}
|
||||
|
||||
|
||||
#include "kdjvu.moc"
|
93
generators/djvu/kdjvu.h
Normal file
93
generators/djvu/kdjvu.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Pino Toscano <toscano.pino@tiscali.it> *
|
||||
* *
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _KDJVU_
|
||||
#define _KDJVU_
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qpixmap.h>
|
||||
#include <qvector.h>
|
||||
|
||||
/**
|
||||
* @brief Qt (KDE) encapsulation of the DjVuLibre
|
||||
*/
|
||||
class KDjVu : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KDjVu();
|
||||
~KDjVu();
|
||||
|
||||
/**
|
||||
* A DjVu page.
|
||||
*/
|
||||
class Page
|
||||
{
|
||||
friend class KDjVu;
|
||||
|
||||
public:
|
||||
~Page();
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
int dpi() const;
|
||||
int orientation() const;
|
||||
|
||||
private:
|
||||
Page();
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_dpi;
|
||||
int m_orientation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the file \p fileName, closing the old one if necessary.
|
||||
*/
|
||||
bool openFile( const QString & fileName );
|
||||
/**
|
||||
* Close the file currently opened, if any.
|
||||
*/
|
||||
void closeFile();
|
||||
|
||||
/**
|
||||
* The pages of the current document, or an empty vector otherwise.
|
||||
* \note KDjVu handles the pages, so you don't need to delete them manually
|
||||
* \return a vector with the pages of the current document
|
||||
*/
|
||||
const QVector<KDjVu::Page*> &pages() const;
|
||||
|
||||
/**
|
||||
* Get the metadata for the specified \p key, or a null string otherwise.
|
||||
*/
|
||||
QString getMetaData( const QString & key ) const;
|
||||
|
||||
// pixmap handling
|
||||
/**
|
||||
* Check if the pixmap for the specified \p page with the specified
|
||||
* \p width, \p height and \p rotation is already in cache, and returns
|
||||
* it. If not, a null pixmap is returned.
|
||||
*/
|
||||
QPixmap pixmap( int page, int width, int height, int rotation );
|
||||
/**
|
||||
* Request to load the pixmap for \p page having the specified \p width,
|
||||
* \p height and \p rotation. It will emit pixmapGenerated() when done.
|
||||
*/
|
||||
void requestPixmap( int page, int width, int height, int rotation );
|
||||
|
||||
signals:
|
||||
void pixmapGenerated( int page, const QPixmap & pix );
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
#endif
|
11
generators/djvu/libokularGenerator_djvu.desktop
Normal file
11
generators/djvu/libokularGenerator_djvu.desktop
Normal file
|
@ -0,0 +1,11 @@
|
|||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Service
|
||||
Name=djvu
|
||||
Comment=DjVu backend for oKular
|
||||
ServiceTypes=oKular/Generator
|
||||
MimeType=image/x-djvu
|
||||
X-KDE-Library=libokularGenerator_djvu.la
|
||||
X-KDE-Priority=2
|
||||
X-KDE-oKularAPIVersion=1
|
||||
X-KDE-oKularHasInternalSettings=false
|
Loading…
Reference in a new issue