天天看點

采用qt技術,開發OFD電子文檔閱讀器

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

程式下載下傳位址:csdn:https://download.csdn.net/download/qq_29939347/18835959;

功能簡述:

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

采用qt技術,開發OFD電子文檔閱讀器

注釋編輯ofd

采用qt技術,開發OFD電子文檔閱讀器

開發思路解析

ofd閱讀器顯示的内容包括:文字、圖形、圖等,稱之為圖元;閱讀器可能需要顯示成千上萬個圖元。采用qt完成此功能,有多重方案可供選擇,選擇方案時必須考慮下列因素:1)顯示的性能。2)圖元與滑鼠鍵盤的互動。我選擇了“Graphics View Framework 圖形視圖架構”;程式處理的邏輯見下圖:

采用qt技術,開發OFD電子文檔閱讀器

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作為一款優秀的跨平台架構,為友善我們開發提供了大量的類庫。在充分了解ofd的基礎上,配合qt的“Graphics View Framework”架構,開發ofd閱讀器并非遙不可及。目前該閱讀器僅完成了基本的功能,後續會逐漸完善,敬請期待。

繼續閱讀