You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
8.1 KiB
237 lines
8.1 KiB
/** |
|
\mainpage Okular, the unified document viewer |
|
|
|
\section okular_overview Overview |
|
|
|
- \ref okular_history |
|
- \ref okular_design |
|
- \ref okular_generators |
|
- <a href="http://www.okular.org">Website</a> |
|
|
|
\authors Tobias König <tokoe@kde.org> |
|
|
|
\licenses \lgpl |
|
|
|
\page okular_history Historical background |
|
|
|
Okular is the successor of <a href="kpdf.kde.org">kpdf</a>, the PDF viewer in KDE 3. |
|
kpdf was refactored and extended in a Google Summer of Code project to support not only |
|
viewing PDF but also other types of document, e.g. PostScript files, images and many more. |
|
|
|
\page okular_design The Design of Okular |
|
|
|
To support a wide range of document formats, Okular was designed in a modular way, so you |
|
have the following components: |
|
|
|
\li \ref Shell |
|
\li \ref Part |
|
\li \ref Okular::Document Class |
|
\li \ref Okular::Generator |
|
|
|
The shell is the application which is started by the user as standalone application and |
|
which embedds the part. The part contains all gui elements of Okular, for example the |
|
content list, the bookmark manager, menus and the graphical view of the document class. |
|
The document class is an abstract presentation of the document content. It contains information |
|
about every page of the document, its size, orientation etc. |
|
|
|
But somehow the document class must retrieve these information from the various types of documents. |
|
This is the task of the Generators. Generators are plugins which are loaded at runtime and which |
|
have the knowledge about the internal structure of the different document types. |
|
They extract the needed information from the documents, convert the data into a common format and |
|
pass them to the document class. |
|
|
|
Currently Generators for the following document types are available: |
|
|
|
\li Portable Document Format (PDF) |
|
\li PostScript |
|
\li Device Independent Format (DVI) |
|
\li DeJaVu Format |
|
\li Comic Books |
|
\li Images (JPEG, PNG, GIF, and many more) |
|
\li TIFF Image Format |
|
\li FictionBook Format |
|
\li Plucker Format |
|
\li OpenDocument Text Format |
|
\li Microsofts CHM Format |
|
\li Microsofts XML Document Format |
|
|
|
Now the questions is how can these various formats be represented in a unified way? |
|
Okular provides features like rotation, text search and extraction, zooming and many more, so how |
|
does it match with the different capabilities of the formats? |
|
|
|
\section okular_design_basics Basics of Generators |
|
|
|
Lets start with the smallest commonness of all document formats: |
|
|
|
\li they have pages (one ore more) of a given size |
|
\li pages can be represented as pictures |
|
|
|
So the first thing every Generator must support is to return the number of pages of a document. |
|
Furthermore it must be able to return the picture of a page at a requested size. |
|
|
|
For vector based document formats (e.g. PDF or PostScript) the Generators can render the page for |
|
the requested size, for static documents formats (e.g. images), the Generator must scale the |
|
content according to the requested size, so when you zoom a page in Okular, the Generators are |
|
just asked to return the page for the zoomed size. |
|
|
|
When the document class has retrieved the page pictures from the Generators, it can do further |
|
image manipulation on it, for example rotating them or applying fancy effects. |
|
|
|
\section okular_design_text_support Generators with Text support |
|
|
|
Some document formats however support more functionality than just representing a page as an image. |
|
PDF, PostScript, DVI and DeJaVu for example contains a machine readable representation of the |
|
included text. For those document formats Okular provides additional features like text search, |
|
text extraction and text selection. |
|
|
|
How is that supported by the Generators? |
|
|
|
To access the text from the documents the generators must extract it somehow and make it available |
|
to the document class. However for the text selection feature the document class must also know <em>where</em> |
|
the extracted text is located on the page. For a zoom factor of 100% the absolute position of |
|
the text in the document can be used, however for larger or smaller zoom factors the position |
|
must be recalculated. To make this calculation as easy as possible, the Generators return an |
|
abstract represtentation (\ref Okular::TextPage) of the text which includes every character together |
|
with its <em>normalized</em> position. Normalized means that the width and height of the page is |
|
in the range of 0 to 1, so a character in the middle of the page is at x=0.5 and y=0.5. |
|
|
|
So when you want to know where this character is located on the page which is zoomed at 300%, you just |
|
multiply the position by 3 * page width (and page height) and get the absolute position for this zoom level. |
|
|
|
This abstract text representation also allows an easy rotation of the coordinates, so that text selection |
|
is available on rotated pages as well. |
|
|
|
\section okular_design_meta_information Meta Information |
|
|
|
Most documents have additional meta information like the name of the author, date of creation, |
|
version number etc. These information can be retrieved by the generator as well and will be |
|
shown by Okular in the document properties dialog. |
|
|
|
\page okular_generators How to implement a Generator |
|
|
|
The power of Okular is its extensibility by Generator plugins. This section will describe how to |
|
implement your own plugin for a new document type. |
|
|
|
To provide a short overview and don't reimplementing an existing generator we'll work on a Generator |
|
for the Magic document format, a non existing, pure virtual format :) |
|
|
|
Lets assume we have some helper class (MagicDocument) which provides the following functionality for this |
|
document format: |
|
|
|
\li Loading a document |
|
\li Retrieving number of pages |
|
\li Returning a fixed size picture representation of a page |
|
|
|
The class API looks like this |
|
|
|
\code |
|
class MagicDocument |
|
{ |
|
public: |
|
MagicDocument(); |
|
~MagicDocument(); |
|
|
|
bool loadDocument( const QString &fileName ); |
|
|
|
int numberOfPages() const; |
|
|
|
QSize pageSize( int pageNumber ) const; |
|
|
|
QImage pictureOfPage( int pageNumber ) const; |
|
|
|
private: |
|
... |
|
}; |
|
\endcode |
|
|
|
The methods should be self explaining, loadDocument() loads a document file and returns false on error, |
|
numberOfPages() returns the number of pages, pageSize() returns the size of the page and pictureOfPage() |
|
returns the picture representation of the page. |
|
|
|
Our first version of our Generator is a basic one which just provides page pictures to the document class. |
|
|
|
The API of the basic Generator looks like this: |
|
|
|
\code |
|
#include "magicdocument.h" |
|
|
|
#include <okular/core/generator.h> |
|
|
|
class MagicGenerator : public Okular::Generator |
|
{ |
|
public: |
|
MagicGenerator(); |
|
~MagicGenerator(); |
|
|
|
bool loadDocument( const QString &fileName, QVector<Okular::Page*> &pages ); |
|
bool closeDocument(); |
|
|
|
bool canGeneratePixmap() const; |
|
void generatePixmap( Okular::PixmapRequest *request ); |
|
|
|
private: |
|
MagicDocument mMagicDocument; |
|
}; |
|
|
|
\endcode |
|
|
|
The implementation like this: |
|
|
|
\code |
|
#include <okular/core/page.h> |
|
|
|
#include "magicgenerator.h" |
|
|
|
OKULAR_EXPORT_PLUGIN(MagicGenerator) |
|
|
|
MagicGenerator::MagicGenerator() |
|
: Generator() |
|
{ |
|
} |
|
|
|
MagicGenerator::~MagicGenerator() |
|
{ |
|
} |
|
|
|
bool MagicGenerator::loadDocument( const QString &fileName, QVector<Okular::Page*> &pages ) |
|
{ |
|
if ( !mMagicDocument.loadDocument( fileName ) ) { |
|
emit error( i18n( "Unable to load document" ), -1 ); |
|
return false; |
|
} |
|
|
|
pagesVector.resize( mMagicDocument.numberOfPages() ); |
|
|
|
for ( int i = 0; i < mMagicDocument.numberOfPages(); ++i ) { |
|
const QSize size = mMagicDocument.pageSize( i ); |
|
|
|
Okular::Page * page = new Okular::Page( i, size.width(), size.height(), Okular::Rotation0 ); |
|
pages[ i ] = page; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool MagicGenerator::closeDocument() |
|
{ |
|
return true; |
|
} |
|
|
|
bool MagicGenerator::canGeneratePixmap() const |
|
{ |
|
return true; |
|
} |
|
|
|
void MagicGenerator::generatePixmap( Okular::PixmapRequest *request ) |
|
{ |
|
QImage image = mMagicDocument.pictureOfPage( request->pageNumber() ); |
|
|
|
image = image.scaled( request->width(), request->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); |
|
|
|
request->page()->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( image ) ) ); |
|
|
|
signalPixmapRequestDone( request ); |
|
} |
|
|
|
\endcode |
|
*/
|
|
|