http://shuizhuyuanluo.blog.163.com/blog ... 410503793/
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。
/*******************************************************************************************************************************/
class MagicDocument
{
public:
MagicDocument();
~MagicDocument();
bool loadDocument( const QString &fileName );
int numberOfPages() const;
QSize pageSize( int pageNumber ) const;
QImage pictureOfPage( int pageNumber ) const;
private:
...
};
後端的 API 是這樣子的:
##########################################################################
#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 );
}
##########################################################################