okular 为了支持各种格式的文档,采用了模块化的设计思路。
okular 可分为四个部分:
外壳(Shell)
部件(Part)
文档对象(Okular::Document)
后端(Okular::Generator)
外壳只是作为独立启动 okular 是嵌入部件的部分;部件主要负责呈现,比如各种书签,内容列表,菜单等;文档对象是个抽象概念,包含了文档的各个页面内容,尺寸方向等。
文档对象必须从后端获取这些内容信息,后端得把文档文件本身解析并转换为 okular 内部用于展示的通用模式,再交给文档对象。
那么内部使用的通用模式是如何的呢?看看各种文档有什么共同点吧。大家都有“页”这个概念,哪怕是图片也可以看成只有一页的文档。每个页面都有大小尺寸方向。最终,页面都可以转换成图片给用户看,就像把内容打印在一张纸上。后端最基本的任务就是得知这个文档一共有几页,然后需要转换为图片。对于放大缩小这类需求,一些基于矢量图形的文档,例如 PDF 和 PostScript,后端只需要将矢量图形渲染成需要的尺寸就可以了,而一些静态的内容,例如图像文件,后端就必须进行缩放操作再交给文档对象。
文档对象从后端获取页面图片之后便可以进行一些附加操作了,比如应用切换的特效和旋转等等。
有些格式支持从文档内容中获取文本,比如 PDF。okular 要获取这些文字,是通过用户使用鼠标选取一块区域然后再获得该区域中的文本。这种位置到文字的方式会因为缩放的关系而无法直接确定。okular 定义了一种 0 到 1 的绝对比例坐标,按照比例坐标乘以缩放系数得到对应的实际位置。比如有个文字在页面中央的位置,那么坐标即为 (0.5, 0.5)。
此外,后端还会负责获取文档的元数据信息。如文档作者、创建日期、版本、内容排版、书签、批注。
okular 的强大之处就在于后端插件的可扩展性。接下来将为你展示如何自己写一个 okular 后端。
示例中的 Magic 文档格式是假象中的东西,实际上是没有的。
最基本的后端:装入文档,获取页数,返回指定尺寸的页面图片
以下部分纯照抄,来自 okular team。
/*******************************************************************************************************************************/
##########################################################################
okular 可分为四个部分:
外壳(Shell)
部件(Part)
文档对象(Okular::Document)
后端(Okular::Generator)
外壳只是作为独立启动 okular 是嵌入部件的部分;部件主要负责呈现,比如各种书签,内容列表,菜单等;文档对象是个抽象概念,包含了文档的各个页面内容,尺寸方向等。
文档对象必须从后端获取这些内容信息,后端得把文档文件本身解析并转换为 okular 内部用于展示的通用模式,再交给文档对象。
那么内部使用的通用模式是如何的呢?看看各种文档有什么共同点吧。大家都有“页”这个概念,哪怕是图片也可以看成只有一页的文档。每个页面都有大小尺寸方向。最终,页面都可以转换成图片给用户看,就像把内容打印在一张纸上。后端最基本的任务就是得知这个文档一共有几页,然后需要转换为图片。对于放大缩小这类需求,一些基于矢量图形的文档,例如 PDF 和 PostScript,后端只需要将矢量图形渲染成需要的尺寸就可以了,而一些静态的内容,例如图像文件,后端就必须进行缩放操作再交给文档对象。
文档对象从后端获取页面图片之后便可以进行一些附加操作了,比如应用切换的特效和旋转等等。
有些格式支持从文档内容中获取文本,比如 PDF。okular 要获取这些文字,是通过用户使用鼠标选取一块区域然后再获得该区域中的文本。这种位置到文字的方式会因为缩放的关系而无法直接确定。okular 定义了一种 0 到 1 的绝对比例坐标,按照比例坐标乘以缩放系数得到对应的实际位置。比如有个文字在页面中央的位置,那么坐标即为 (0.5, 0.5)。
此外,后端还会负责获取文档的元数据信息。如文档作者、创建日期、版本、内容排版、书签、批注。
okular 的强大之处就在于后端插件的可扩展性。接下来将为你展示如何自己写一个 okular 后端。
示例中的 Magic 文档格式是假象中的东西,实际上是没有的。
最基本的后端:装入文档,获取页数,返回指定尺寸的页面图片
以下部分纯照抄,来自 okular team。
/*******************************************************************************************************************************/
class MagicDocument后端的 API 是这样子的:
{
public:
MagicDocument();
~MagicDocument();
bool loadDocument( const QString &fileName );
int numberOfPages() const;
QSize pageSize( int pageNumber ) const;
QImage pictureOfPage( int pageNumber ) const;
private:
...
};
##########################################################################
#include "magicdocument.h"##########################################################################
#include <okular/core/generator.h>
class MagicGenerator : public Okular::Generator
{
public:
MagicGenerator( QObject *parent, const QVariantList &args );
~MagicGenerator();
bool loadDocument( const QString &fileName, QVector<Okular::Page*> &pages );
bool canGeneratePixmap() const;
void generatePixmap( Okular::PixmapRequest *request );
protected:
bool doCloseDocument();
private:
MagicDocument mMagicDocument;
};
#include <okular/core/page.h>##########################################################################
#include "magicgenerator.h"
static KAboutData createAboutData()
{
KAboutData aboutData(...);
// fill the about data
return aboutData;
}
OKULAR_EXPORT_PLUGIN(MagicGenerator, createAboutData())
MagicGenerator::MagicGenerator( QObject *parent, const QVariantList &args )
: Okular::Generator( parent, args )
{
}
MagicGenerator::~MagicGenerator()
{
}
// loadDocument 装入文档文件并获取文档页数。文档内的每个页面都会向 pages 添加一个 Okular::Page 对象,
bool MagicGenerator::loadDocument( const QString &fileName, QVector<Okular::Page*> &pages )
{
if ( !mMagicDocument.loadDocument( fileName ) ) {
emit error( i18n( "Unable to load document" ), -1 );
return false;
}
pages.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::doCloseDocument()
{
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 );
}