前言 ofd作為闆式文檔規範,相當于國産化的pdf。由于pdf标準制定的較早,相關生态也比較完備,市面上的pdf閱讀器種類繁多。國内ofd閱讀器寥寥無幾,作者此前采用wpf開發了一款閱讀器,但該閱讀器隻能在windows上運作。若實作跨平台運作,采用QT開發應該是首選。筆者并無QT開發經驗,但有多年MFC開發經驗,又對ofd研究多年;程式設計到達一定境界考驗的就是思維,在學習QT的過程中,感覺都是熟悉的味道的。邊學習邊開發,終于完成了一款簡易的ofd閱讀器。簡述開發思路,希望對讀者有所啟發。
程式下載下傳位址:csdn:https://download.csdn.net/download/qq_29939347/18835959;
功能簡述:
閱讀器實作了縮放、旋轉、選中、複制、單雙頁顯示等功能。

注釋編輯ofd
開發思路解析
ofd閱讀器顯示的内容包括:文字、圖形、圖等,稱之為圖元;閱讀器可能需要顯示成千上萬個圖元。采用qt完成此功能,有多重方案可供選擇,選擇方案時必須考慮下列因素:1)顯示的性能。2)圖元與滑鼠鍵盤的互動。我選擇了“Graphics View Framework 圖形視圖架構”;程式處理的邏輯見下圖:
ofd解壓:
ofd本身就是壓縮檔案,和zip字尾的檔案處理完全一樣。解壓縮采用QuaZip庫。作者在此庫基礎上作了進一步封裝,使之更便于使用。
OfdFileReader::OfdFileReader()
{
_pZipInfo = nullptr;
_file = nullptr;
}
OfdFileReader::~OfdFileReader()
{
MemManage::instance()->Delete(_pZipInfo);
MemManage::instance()->Delete(_file);
}
bool OfdFileReader::Open(QString fileName)
{
MemManage::instance()->Delete( _file);
_file =MemManage::instance()->New<QFile,QString>(fileName);
if (!_file->open(QIODevice::ReadOnly))
return false;
_ofdFileName = fileName;
return Open(_file);
}
bool OfdFileReader::Open(QIODevice *ioDevice)
{
MemManage::instance()->Delete(_pZipInfo);
_pZipInfo =MemManage::instance()->New<QuaZip,QIODevice*>(ioDevice);
bool isOpen = _pZipInfo->open(QuaZip::mdUnzip);
if(!isOpen)
return false;
_listFilePath.clear();
GetAllZipInfo();
return true;
}
QString OfdFileReader::GetFileFullName()
{
return _ofdFileName;
}
QString OfdFileReader::GetFileShortName()
{
QFileInfo fileInfo(_ofdFileName);
return fileInfo.baseName();
}
void OfdFileReader::GetAllZipInfo()
{
for (bool f = _pZipInfo->goToFirstFile(); f;f=_pZipInfo->goToNextFile())
{
QString relativePath = _pZipInfo->getCurrentFileName();
_listFilePath.append(relativePath);
//qDebug() << relativePath;
}
}
int OfdFileReader::GetFileCount()
{
return _listFilePath.count();
}
QString OfdFileReader::GetFilePath(int index)
{
return _listFilePath[index];
}
QStringList OfdFileReader::GetFilePathList()
{
return _listFilePath;
}
QByteArray OfdFileReader::GetFileContent(const QString& relativePath)
{
if(relativePath.size()==0)
{
QByteArray empty;
return empty;
}
_pZipInfo->setCurrentFile(relativePath);
QuaZipFile zFile(_pZipInfo,0);
if(!zFile.open(QIODevice::ReadOnly))
{
QByteArray empty;
return empty;
}
QByteArray ba = zFile.readAll();
zFile.close();
return ba;
}
xml解析
ofd主要是由xml文本和資源檔案組成。qt解析xml有兩個庫:DOM解析(QDomDocument)和流式解析(QXmlStreamReader)。DOM解析使用起來簡單,但是性能慢;流式解析反之。從性能角度考慮,作者采用了流式解析的方法。
Qt Graphics View Framework 圖形視圖架構
繪制大量圖元最佳方案就是采用qt提供的“Graphics View Framework”架構。此架構確定高效的繪制大量圖元,又能快速的根據區域定位到圖元。該架構采用面向對象的方法處理圖元,減輕了開發難度。圖元的描述稱之為scene,圖元顯示為view。一個scene可以由多個view展示。首先需要将ofd頁面中文字、線、圖等元素轉換成對應的scene。以顯示文字為例,定義類型 class OfdVisualItemText : public QGraphicsObject。需要實作兩個虛函數:
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
paint函數根據scene資料,繪制對應的文字。第一次繪制時,須記錄每個文字的區域;滑鼠滑動時,根據選擇區域與每個文字的關系,确定文字是否被選中。
void OfdVisualItemText::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setRenderHint(QPainter::TextAntialiasing);
painter->setBrush(Qt::black);
painter->setPen(Qt::black);
SetPen(painter);
SetFont(painter);
//SetCTMTransfer(painter);
if(_isFirstPaint)
{
SetCTMTransfer();
}
if(_isSelect)
{
QList<QRectData*> selectData = _boundingRectManage.GetSelectData(_selectPolygon);
foreach(QRectData *item,selectData)
{
painter->fillRect(item->rect,*OfdViewParam::TextSelectBrush);
}
}
OfdPageItemText *itemText = (OfdPageItemText*)_ofdPageItem;
int charCount = itemText->TextCode.GetCharCount();
QChar charItem;
float x;
float y;
QRectF textboundingRect;
QRectF textClipRect;
float baseline = GetBaseline();
for(int i=0;i<charCount;i++)
{
itemText->TextCode.GetChar(i,charItem,x,y);
double xPixel = OfdConvert::OfdMMToPixel(x);
double yPixel = OfdConvert::OfdMMToPixel(y);
QString textChar(charItem);
textClipRect.setRect(xPixel,yPixel-baseline,10000,10000);
painter->drawText(textClipRect,0,textChar,&textboundingRect);
AdjustTextRect(textboundingRect);
}
_isFirstPaint = false;
}
閱讀器操作截圖
後記:理清思路,選對架構是成功的第一步。qt作為一款優秀的跨平台架構,為友善我們開發提供了大量的類庫。在充分了解ofd的基礎上,配合qt的“Graphics View Framework”架構,開發ofd閱讀器并非遙不可及。目前該閱讀器僅完成了基本的功能,後續會逐漸完善,敬請期待。