Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 605ae18b authored by Jakub Stachowski's avatar Jakub Stachowski

Initial import of mobipocket generator


svn path=/trunk/playground/graphics/okular/mobipocket/; revision=882081
parents
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
project(okular-mobi-generator)
ENABLE_TESTING()
find_package(KDE4 REQUIRED)
include_directories(${KDE4_INCLUDES} ${QT_INCLUDES}
${CMAKE_CURRENT_SOURCE_DIR}
)
########### next target ###############
find_package(SharedMimeInfo REQUIRED)
set(okularGenerator_mobi_PART_SRCS
converter.cpp
mobipocket.cpp
mobidocument.cpp
generator_mobi.cpp
)
kde4_add_plugin(okularGenerator_mobi ${okularGenerator_mobi_PART_SRCS})
target_link_libraries(okularGenerator_mobi okularcore ${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} )
install(
FILES x-mobipocket.xml
DESTINATION ${XDG_MIME_INSTALL_DIR}
)
update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR})
#!/bin/sh
$XGETTEXT $(find . -name "*.cpp") -o $podir/okular_mobi.pot
- images are broken in some files
- better error handling
- tests for Mobipocket classes
- anchors (a filepos=)
- handle files compression with Huffman encoding
- decryption for DRMed files
/***************************************************************************
* 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/QTextFrame>
#include <QTextDocumentFragment>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <qmobi.h>
#include <klocale.h>
#include <okular/core/action.h>
using namespace Mobi;
Converter::Converter()
{
}
Converter::~Converter()
{
}
QTextDocument* Converter::convert( const QString &fileName )
{
MobiDocument* newDocument=new MobiDocument(fileName);
if (!newDocument->isValid()) {
emit error(i18n("Error while opening the EPub document."), -1);
delete newDocument;
return NULL;
}
newDocument->setPageSize(QSizeF(600, 800));
QTextFrameFormat frameFormat;
frameFormat.setMargin( 20 );
QTextFrame *rootFrame = newDocument->rootFrame();
rootFrame->setFrameFormat( frameFormat );
return newDocument;
}
/***************************************************************************
* 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"
class QTextCursor;
namespace Mobi {
class Converter : public Okular::TextDocumentConverter
{
public:
Converter();
~Converter();
virtual QTextDocument *convert( const QString &fileName );
};
}
#endif
/***************************************************************************
* 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_epub",
"okular_epub",
ki18n("Mobipocket Backend"),
"0.1",
ki18n("An mobipocket backend"),
KAboutData::License_GPL,
ki18n("© 2008 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 )
{
}
/***************************************************************************
* 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
[Desktop Entry]
Type=Service
Name=Mobipocket document
Comment=Mobipocket backend for Okular
X-KDE-ServiceTypes=okular/Generator
MimeType=application/x-mobipocket;
X-KDE-Library=okularGenerator_mobi
X-KDE-Priority=1
X-KDE-okularAPIVersion=1
X-KDE-okularHasInternalSettings=false
/***************************************************************************
* 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 "qmobi.h"
#include <QFile>
#include <QRegExp>
#include <kdebug.h>
using namespace Mobi;
MobiDocument::MobiDocument(const QString &fileName) : QTextDocument()
{
file = new QFile(fileName);
file->open(QIODevice::ReadOnly);
doc = new Mobipocket::Document(file);
if (doc->isValid()) setHtml(fixMobiMarkup(doc->text()));
}
bool MobiDocument::isValid() const
{
return doc->isValid();
}
MobiDocument::~MobiDocument()
{
delete doc;
delete file;
}
QVariant MobiDocument::loadResource(int type, const QUrl &name)
{
kDebug() << "Requested resource: " << type << " URL " << name;
if (type!=QTextDocument::ImageResource || name.scheme()!=QString("pdbrec")) return QVariant();
bool ok;
quint16 recnum=name.path().mid(1).toUShort(&ok);
kDebug() << "Path" << name.path().mid(1) << " Img " << recnum << " all imgs " << doc->imageCount();
if (!ok || recnum>=doc->imageCount()) return QVariant();
QVariant resource;
resource.setValue(doc->getImage(recnum));
addResource(type, name, resource);
return resource;
}
QString MobiDocument::fixMobiMarkup(const QString& data)
{
QRegExp imgs("<[iI][mM][gG].*recindex=\"([0-9]*)\".*>");
imgs.setMinimal(true);
QString ret=data;
ret.replace(imgs,"<img src=\"pdbrec:/\\1\">");
ret.replace("<mbp:pagebreak/>","<p style=\"page-break-after:always\"></p>");
//FIXME: anchors
return ret;
}
/***************************************************************************
* 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;
}
namespace Mobi {
class MobiDocument : public QTextDocument {
public:
MobiDocument(const QString &fileName);
bool isValid() const;
~MobiDocument();
protected:
virtual QVariant loadResource(int type, const QUrl &name);
private:
QString fixMobiMarkup(const QString& data);
Mobipocket::Document *doc;
QFile* file;
};
}
#endif
/***************************************************************************
* Copyright (C) 2008 by Jakub Stachowski <qbast@go2.pl> *
* *
* RLE decompressor based on FBReader *
* Copyright (C) 2004-2008 Geometer Plus <contact@geometerplus.com> *
* *
* 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 <mobipocket.h>
#include <QtCore/QIODevice>
#include <QtCore/QtEndian>
#include <QtCore/QBuffer>
#include <kdebug.h>
static unsigned char TOKEN_CODE[256] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
};
namespace Mobipocket {
class NOOPDecompressor : public Decompressor
{
public:
QByteArray decompress(const QByteArray& data) { return data; }
};
class RLEDecompressor : public Decompressor
{
public:
QByteArray decompress(const QByteArray& data);
};
QByteArray RLEDecompressor::decompress(const QByteArray& data)
{
QByteArray ret;
ret.reserve(8192);
unsigned char token;
unsigned short copyLength, N, shift;
unsigned short shifted;
int i=0;
int maxIndex=data.size()-1;
while (i<data.size()) {
token = data.at(i++);
switch (TOKEN_CODE[token]) {
case 0:
ret.append(token);
break;
case 1:
if ((i + token > maxIndex) ) {
goto endOfLoop;
}
ret.append(data.mid(i,token));
i+=token;
break;
case 2:
ret.append(' ');
ret.append(token ^ 0x80);
break;
case 3:
if (i + 1 > maxIndex) {
goto endOfLoop;
}
// N = (token << 8) + data.at(i++);
N = token;
N<<=8;
N+=(unsigned char)data.at(i++);
copyLength = (N & 7) + 3;
shift = (N & 0x3fff) / 8;
shifted = ret.size()-shift;
if (shifted>(ret.size()-1)) goto endOfLoop;
for (int i=0;i<copyLength;i++) ret.append(ret.at(shifted+i));
break;
}
}
endOfLoop:
return ret;
}
/////////////////////////////////////////////
struct PDBPrivate {
QList<quint32> recordOffsets;
QIODevice* device;
QString fileType;
QString name;
quint16 nrecords;
bool valid;
void init();
};
void PDBPrivate::init()
{
valid=true;
quint16 word;
quint32 dword;
device->seek(0);
name=QString::fromLatin1(device->read(32));
device->seek(0x3c);
fileType=QString::fromLatin1(device->read(8));
device->seek(0x4c);
device->read((char*)&word,2);
nrecords=qFromBigEndian(word);
for (int i=0;i<nrecords;i++) {
device->read((char*)&dword,4);
recordOffsets.append(qFromBigEndian(dword));
device->read((char*)&dword,4);
}
}
PDB::PDB(QIODevice* dev) : d(new PDBPrivate)
{
d->device=dev;
d->init();
}
QByteArray PDB::getRecord(int i) const
{
if (i>=d->nrecords) return QByteArray();
quint32 offset=d->recordOffsets[i];
bool last=(i==(d->nrecords-1));
quint32 size=0;
if (last) size=d->device->size()-offset;
else size=d->recordOffsets[i+1]-offset;
d->device->seek(offset);
return d->device->read(size);
}
QString PDB::name() const
{
return d->name;
}
bool PDB::isValid() const
{
return d->valid;
}
int PDB::recordCount() const
{
return d->nrecords;
}
////////////////////////////////////////////
struct DocumentPrivate
{
DocumentPrivate(QIODevice* d) : pdb(d), valid(true) {}
PDB pdb;
Decompressor* dec;
quint16 ntextrecords;
bool valid;
void init() {
valid=pdb.isValid();
if (!valid) return;
QByteArray mhead=pdb.getRecord(0);
if (mhead[0]!=(char)0) {}
switch (mhead[1]) {
case 1 : dec = new NOOPDecompressor(); break;
case 2 : dec = new RLEDecompressor(); break;
default : dec=0; {}
}
ntextrecords=(unsigned char)mhead[8];
ntextrecords<<=8;
ntextrecords+=(unsigned char)mhead[9];
}
};
Document::Document(QIODevice* dev) : d(new DocumentPrivate(dev))
{
d->init();
}
QString Document::text() const
{
QByteArray whole;
for (int i=1;i<d->ntextrecords;i++)
whole+=d->dec->decompress(d->pdb.getRecord(i));
return QString::fromUtf8(whole);
}
int Document::imageCount() const
{
//FIXME: don't count FLIS and FCIS records
return d->pdb.recordCount()-d->ntextrecords;
}
bool Document::isValid() const
{
return d->pdb.isValid();
}
QImage Document::getImage(int i) const
{
QByteArray rec=d->pdb.getRecord(d->ntextrecords+i);
kDebug() << "Want IMG " << i;
kDebug() << rec[0] << rec[1] << rec[2] << rec[3];
//FIXME: check if i is in range
return QImage::fromData(rec);
}
}
/***************************************************************************
* 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 <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QMap>
#include <QtGui/QImage>
class QIODevice;
namespace Mobipocket {
struct PDBPrivate;
class PDB {
public:
PDB(QIODevice* dev);
QString fileType() const;
int recordCount() const;
QByteArray getRecord(int i) const;
QString name() const;
bool isValid() const;
private:
PDBPrivate* const d;
};
class Decompressor {
public:
virtual QByteArray decompress(const QByteArray& data) = 0;
virtual ~Decompressor() {}
};
struct DocumentPrivate;
class Document {
public:
Document(QIODevice* dev);
QMap<QString,QString> metadata() const;
QString text() const;
int imageCount() const;
QImage getImage(int i) const;
bool isValid() const;
private:
DocumentPrivate* const d;
};
}
[Desktop Entry]
MimeType=application/x-mobipocket;
Terminal=false
Name=Okular
Name[x-test]=xxOkularxx
GenericName=Document Viewer
GenericName[bg]=Визуализатор
GenericName[ca]=Visualitzador de documents
GenericName[cs]=Prohlížeč dokumentů
GenericName[da]=Dokumentfremviser
GenericName[de]=Dokumentenbetrachter
GenericName[el]=Προβολέας εγγράφων
GenericName[eo]=Dokumenta rigardilo
GenericName[es]=Visor de documentos
GenericName[et]=Dokumendinäitaja
GenericName[fa]=مشاهده‌گر سند
GenericName[fi]=Asiakirjan näyttöohjelma
GenericName[fr]=Visionneuse de document
GenericName[ga]=Amharcán Cáipéisí
GenericName[gl]=Visor de documentos
GenericName[hi]=दस्तावेज़ प्रदर्शक
GenericName[hu]=Dokumentumnézegető
GenericName[is]=Skjalaskoðari
GenericName[it]=Visore di documenti
GenericName[ja]=文書ビューア
GenericName[kk]=Құжатты қарау
GenericName[km]=កម្មវិធី​មើល​ឯកសារ
GenericName[ko]=문서 뷰어
GenericName[lv]=Dokumentu skatītājs
GenericName[nb]=Dokumentviser
GenericName[nds]=Dokmentkieker
GenericName[ne]=कागजात दर्शक
GenericName[nl]=Documentenviewer
GenericName[nn]=Dokumentvisar
GenericName[oc]=Visualizaire de documents
GenericName[pa]=ਡੌਕੂਮੈਂਟ ਦਰਸ਼ਕ
GenericName[pl]=Przeglądarka dokumentów
GenericName[pt]=Visualizador de Documentos
GenericName[pt_BR]=Visualizador de documentos
GenericName[ru]=Просмотр документов
GenericName[sl]=Pregledovalnik dokumentov
GenericName[sv]=Dokumentvisare
GenericName[th]=เครื่องมือแสดงเอกสาร
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
[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;
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-mobipocket">
<comment>Mobipocket document</comment>
<glob pattern="*.prc"/>
<glob pattern="*.mobi"/>
</mime-type>
</mime-info>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment