采用QT,開發跨平台OFD閱讀器


前言 ofd作為板式文檔規范,相當於國產化的pdf。由於pdf標准制定的較早,相關生態也比較完備,市面上的pdf閱讀器種類繁多。國內ofd閱讀器寥寥無幾,作者此前采用wpf開發了一款閱讀器,但該閱讀器只能在windows上運行。若實現跨平台運行,采用QT開發應該是首選。筆者並無QT開發經驗,但有多年MFC開發經驗,又對ofd研究多年;編程到達一定境界考驗的就是思維,在學習QT的過程中,感覺都是熟悉的味道的。邊學習邊開發,終於完成了一款簡易的ofd閱讀器。簡述開發思路,希望對讀者有所啟發。

獲取最新版本,加入qq群:QQ:618168615。

 

功能簡述:

 閱讀器實現了縮放、旋轉、選中、復制、單雙頁顯示等功能。

 

注釋編輯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閱讀器並非遙不可及。目前該閱讀器僅完成了基本的功能,后續會逐步完善,敬請期待。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM