okular 的設計和後端

KDE 是一個強大的圖形桌面環境,各項關於 KDE 使用上的問題或討論歡迎在此提出。

版主: AceLan, Franklin

okular 的設計和後端

文章訪客 » 週二 3月 23, 2010 8:38 pm

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 );
}
##########################################################################
訪客
 

回到 KDE 一般討論

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 1 位訪客