Mobipocket support for okular, thumbnails and strigi

svn path=/trunk/KDE/kdegraphics/okular/; revision=928589
This commit is contained in:
Jakub Stachowski 2009-02-19 16:17:58 +00:00
commit a48680c1e5
11 changed files with 438 additions and 0 deletions

View file

@ -72,3 +72,5 @@ endif(JPEG_FOUND AND ZLIB_FOUND)
if(EPUB_FOUND)
add_subdirectory(epub)
endif(EPUB_FOUND)
add_subdirectory(mobipocket)

View file

@ -0,0 +1,28 @@
find_package(KDE4 REQUIRED)
find_package(Okular REQUIRED)
include_directories(${KDE4_INCLUDES} ${QT_INCLUDES}
${CMAKE_CURRENT_SOURCE_DIR}
${OKULAR_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/../libs/mobipocket
)
########### next target ###############
set(okularGenerator_mobi_PART_SRCS
${PROJECT_SOURCE_DIR}/../libs/mobipocket/mobipocket.cpp
${PROJECT_SOURCE_DIR}/../libs/mobipocket/decompressor.cpp
mobidocument.cpp
generator_mobi.cpp
converter.cpp
)
kde4_add_plugin(okularGenerator_mobi ${okularGenerator_mobi_PART_SRCS})
target_link_libraries(okularGenerator_mobi ${OKULAR_LIBRARIES} ${mobi_LIBRARIES} ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY})
install(TARGETS okularGenerator_mobi DESTINATION ${PLUGIN_INSTALL_DIR})
########### install files ###############
install( FILES libokularGenerator_mobi.desktop okularMobi.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
install( FILES okularApplication_mobi.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )

View file

@ -0,0 +1,107 @@
/***************************************************************************
* Copyright (C) 2008 by Jakub Stachowski <qbast@go2.pl> *
* *
* 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 "converter.h"
#include <QtGui/QAbstractTextDocumentLayout>
#include <QtGui/QTextDocument>
#include <QtGui/QTextBlock>
#include <QtGui/QTextFrame>
#include <QtGui/QTextDocumentFragment>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include "mobipocket.h"
#include <klocale.h>
#include <okular/core/action.h>
using namespace Mobi;
Converter::Converter()
{
}
Converter::~Converter()
{
}
void Converter::handleMetadata(const QMap<Mobipocket::Document::MetaKey,QString> metadata)
{
QMapIterator<Mobipocket::Document::MetaKey,QString> it(metadata);
while (it.hasNext()) {
it.next();
switch (it.key()) {
case Mobipocket::Document::Title: addMetaData(Okular::DocumentInfo::Title, it.value()); break;
case Mobipocket::Document::Author: addMetaData(Okular::DocumentInfo::Author, it.value()); break;
case Mobipocket::Document::Description: addMetaData(Okular::DocumentInfo::Description, it.value()); break;
case Mobipocket::Document::Subject: addMetaData(Okular::DocumentInfo::Subject, it.value()); break;
case Mobipocket::Document::Copyright: addMetaData(Okular::DocumentInfo::Copyright, it.value()); break;
}
}
}
QTextDocument* Converter::convert( const QString &fileName )
{
MobiDocument* newDocument=new MobiDocument(fileName);
if (!newDocument->mobi()->isValid()) {
emit error(i18n("Error while opening the Mobipocket document."), -1);
delete newDocument;
return NULL;
}
if (newDocument->mobi()->hasDRM()) {
emit error(i18n("This book is protected by DRM and can be displayed only on designated device"), -1);
delete newDocument;
return NULL;
}
handleMetadata(newDocument->mobi()->metadata());
newDocument->setPageSize(QSizeF(600, 800));
QTextFrameFormat frameFormat;
frameFormat.setMargin( 20 );
QTextFrame *rootFrame = newDocument->rootFrame();
rootFrame->setFrameFormat( frameFormat );
QMap<QString,QPair<int,int> > links;
QMap<QString,QTextBlock> targets;
// go over whole document and add all <a> tags to links or targets map
for (QTextBlock it = newDocument->begin(); it != newDocument->end(); it = it.next())
for (QTextBlock::iterator fit=it.begin(); !fit.atEnd(); ++fit) {
QTextFragment frag=fit.fragment();
QTextCharFormat format=frag.charFormat();
if (!format.isAnchor()) continue;
//link
if (!format.anchorHref().isEmpty()) links[format.anchorHref()]=
QPair<int,int>(frag.position(), frag.position()+frag.length());
if (!format.anchorNames().isEmpty()) {
// link targets
Q_FOREACH(const QString& name, format.anchorNames())
targets['#'+name]=it;
}
}
// create link actions
QMapIterator<QString,QPair<int,int> > it(links);
while (it.hasNext()) {
it.next();
QUrl u(it.key());
// external or internal link
if (!u.isRelative()) emit addAction(new Okular::BrowseAction(it.key()), it.value().first, it.value().second);
else {
// is there valid target?
if (!targets.contains( it.key() ) || !targets[it.key()].isValid()) continue;
emit addAction(new Okular::GotoAction(QString(), calculateViewport( newDocument, targets[it.key()] )),
it.value().first, it.value().second);
}
}
return newDocument;
}

View file

@ -0,0 +1,32 @@
/***************************************************************************
* Copyright (C) 2008 by Jakub Stachowski <qbast@go2.pl> *
* *
* 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 MOBI_CONVERTER_H
#define MOBI_CONVERTER_H
#include <okular/core/textdocumentgenerator.h>
#include <okular/core/document.h>
#include "mobidocument.h"
#include "mobipocket.h"
namespace Mobi {
class Converter : public Okular::TextDocumentConverter
{
public:
Converter();
~Converter();
virtual QTextDocument *convert( const QString &fileName );
private:
void handleMetadata(const QMap<Mobipocket::Document::MetaKey, QString> metadata);
};
}
#endif

View file

@ -0,0 +1,37 @@
/***************************************************************************
* Copyright (C) 2008 by Ely Levy <elylevy@cs.huji.ac.il> *
* *
* 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_mobi.h"
#include "converter.h"
#include <kaboutdata.h>
static KAboutData createAboutData()
{
KAboutData aboutData(
"okular_mobi",
"okular_mobi",
ki18n("Mobipocket Backend"),
"0.1",
ki18n("A mobipocket backend"),
KAboutData::License_GPL,
ki18n("© 2008-2009 Jakub Stachowski")
);
aboutData.addAuthor(ki18n("Jakub Stachowski"), KLocalizedString(),
"qbast@go2.pl");
return aboutData;
}
OKULAR_EXPORT_PLUGIN( MobiGenerator, createAboutData() )
MobiGenerator::MobiGenerator( QObject *parent, const QVariantList &args )
: Okular::TextDocumentGenerator( new Mobi::Converter, parent, args )
{
}

View file

@ -0,0 +1,20 @@
/***************************************************************************
* Copyright (C) 2008 by Ely Levy <elylevy@cs.huji.ac.il> *
* *
* 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 OKULAR_GENERATOR_MOBI_H
#define OKULAR_GENERATOR_MOBI_H
#include <okular/core/textdocumentgenerator.h>
class MobiGenerator : public Okular::TextDocumentGenerator
{
public:
MobiGenerator( QObject *parent, const QVariantList &args );
~MobiGenerator() {}
};
#endif

View file

@ -0,0 +1,42 @@
[Desktop Entry]
Type=Service
Name=Mobipocket document
Name[el]=Έγγραφο Mobipocket
Name[es]=Documento Mobipocket
Name[et]=Mobipocketi dokument
Name[fr]=Document mobipocket
Name[ga]=Cáipéis Mobipocket
Name[ja]=Mobipocket
Name[nds]=Mobipocket-Dokment
Name[nl]=Mobipocket-document
Name[pt]=Documento Mobipocket
Name[pt_BR]=Documento Mobipocket
Name[sv]=Mobipocket-dokument
Name[tr]=Mobipocket belgesi
Name[uk]=Документ Mobipocket
Name[x-test]=xxMobipocket documentxx
Name[zh_CN]=Mobipocket
Name[zh_TW]=Mobipocket
Comment=Mobipocket backend for Okular
Comment[el]=Σύστημα υποστήριξης Mobipocket για το Okular
Comment[es]=Motor de Mobipocket para Okular
Comment[et]=Okulari Mobipocketi taustaprogramm
Comment[fr]=Moteur Mobipocket pour Okular
Comment[ga]=Inneall Mobipocket le haghaidh Okular
Comment[ja]=Okular Mobipocket
Comment[nds]=Mobipocket-Hülppropgramm för Okular
Comment[nl]=Mobipocket-backend voor Okular
Comment[pt]=Interface do Mobipocket para o Okular
Comment[pt_BR]=Infraestrutura Mobipocket para o Okular
Comment[sv]=Mobipocket-gränssnitt för Okular
Comment[tr]=Okular için Mobipocket arka ucu
Comment[uk]=Сервер Mobipocket для Okular
Comment[x-test]=xxMobipocket backend for Okularxx
Comment[zh_CN]=Okular Mobipocket
Comment[zh_TW]=Okular Mobipocket
X-KDE-ServiceTypes=okular/Generator
MimeType=application/x-mobipocket-ebook;
X-KDE-Library=okularGenerator_mobi
X-KDE-Priority=1
X-KDE-okularAPIVersion=1
X-KDE-okularHasInternalSettings=false

View file

@ -0,0 +1,92 @@
/***************************************************************************
* Copyright (C) 2008 by Jakub Stachowski <qbast@go2.pl> *
* *
* 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 "mobidocument.h"
#include "mobipocket.h"
#include "qfilestream.h"
#include <QtCore/QFile>
#include <QtCore/QRegExp>
#include <kdebug.h>
using namespace Mobi;
MobiDocument::MobiDocument(const QString &fileName) : QTextDocument()
{
file = new Mobipocket::QFileStream(fileName);
doc = new Mobipocket::Document(file);
if (doc->isValid()) setHtml(fixMobiMarkup(doc->text()));
}
MobiDocument::~MobiDocument()
{
delete doc;
delete file;
}
QVariant MobiDocument::loadResource(int type, const QUrl &name)
{
if (type!=QTextDocument::ImageResource || name.scheme()!=QString("pdbrec")) return QVariant();
bool ok;
quint16 recnum=name.path().mid(1).toUShort(&ok);
if (!ok || recnum>=doc->imageCount()) return QVariant();
QVariant resource;
resource.setValue(doc->getImage(recnum-1));
addResource(type, name, resource);
return resource;
}
// starting from 'pos', find position in the string that is not inside a tag
int outsideTag(const QString& data, int pos)
{
for (int i=pos-1;i>=0;i--) {
if (data[i]=='>') return pos;
if (data[i]=='<') return i;
}
return pos;
}
QString MobiDocument::fixMobiMarkup(const QString& data)
{
QString ret=data;
QMap<int,QString> anchorPositions;
static QRegExp anchors("<a(?: href=\"[^\"]*\"){0,1}[\\s]+filepos=['\"]{0,1}([\\d]+)[\"']{0,1}", Qt::CaseInsensitive);
int pos=0;
// find all link destinations
while ( (pos=anchors.indexIn( data, pos ))!=-1) {
int filepos=anchors.cap( 1 ).toUInt( );
if (filepos) anchorPositions[filepos]=anchors.cap(1);
pos+=anchors.matchedLength();
}
// put HTML anchors in all link destinations
int offset=0;
QMapIterator<int,QString> it(anchorPositions);
while (it.hasNext()) {
it.next();
// link pointing outside the document, ignore
if ( (it.key()+offset) >= ret.size()) continue;
int fixedpos=outsideTag(ret, it.key()+offset);
ret.insert(fixedpos,QString("<a name=\"")+it.value()+QString("\">&nbsp;</a>"));
// inserting anchor shifts all offsets after the anchor
offset+=21+it.value().size();
}
// replace links referencing filepos with normal internal links
ret.replace(anchors,"<a href=\"#\\1\"");
// Mobipocket uses strange variang of IMG tags: <img recindex="3232"> where recindex is number of
// record containing image
static QRegExp imgs("<img.*recindex=\"([\\d]*)\".*>", Qt::CaseInsensitive);
imgs.setMinimal(true);
ret.replace(imgs,"<img src=\"pdbrec:/\\1\">");
ret.replace("<mbp:pagebreak/>","<p style=\"page-break-after:always\"></p>");
return ret;
}

View file

@ -0,0 +1,42 @@
/***************************************************************************
* Copyright (C) 2008 by Jakub Stachowski <qbast@go2.pl> *
* *
* 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 MOBI_DOCUMENT_H
#define MOBI_DOCUMENT_H
#include <QTextDocument>
#include <QUrl>
#include <QVariant>
class QFile;
namespace Mobipocket {
class Document;
class QFileStream;
}
namespace Mobi {
class MobiDocument : public QTextDocument {
public:
MobiDocument(const QString &fileName);
~MobiDocument();
Mobipocket::Document* mobi() const { return doc; }
protected:
virtual QVariant loadResource(int type, const QUrl &name);
private:
QString fixMobiMarkup(const QString& data);
Mobipocket::Document *doc;
Mobipocket::QFileStream* file;
};
}
#endif

View file

@ -0,0 +1,28 @@
[Desktop Entry]
MimeType=application/x-mobipocket-ebook;
Terminal=false
Name=Okular
Name[x-test]=xxOkularxx
GenericName=Document Viewer
GenericName[el]=Προβολέας εγγράφων
GenericName[es]=Visor de documentos
GenericName[et]=Dokumentide näitaja
GenericName[fr]=Visonneuse de document
GenericName[ga]=Amharcán Cáipéisí
GenericName[ja]=
GenericName[nds]=Dokmentkieker
GenericName[nl]=Documentviewer
GenericName[pt]=Visualizador de Documentos
GenericName[pt_BR]=Visualizador de documentos
GenericName[sv]=Dokumentvisning
GenericName[tr]=Belge Gösterici
GenericName[uk]=Переглядач документів
GenericName[x-test]=xxDocument Viewerxx
GenericName[zh_CN]=
GenericName[zh_TW]=
Exec=okular %U %i -caption "%c"
Icon=okular
Type=Application
InitialPreference=1
Categories=Qt;KDE;Graphics;Viewer;
NoDisplay=true

View file

@ -0,0 +1,8 @@
[Desktop Entry]
Icon=okular
Name=Okular
Name[x-test]=xxOkularxx
X-KDE-ServiceTypes=KParts/ReadOnlyPart
X-KDE-Library=okularpart
Type=Service
MimeType=application/x-mobipocket-ebook;