admin管理员组

文章数量:1648972

一个简单的观看远程pdf文件的demo

准备第三方库qtpdfium

参考博客:基本用法在这里,包括库的下载和编译

服务端准备

服务端打开文件,选择要展示的pdf文件

全局定义头文件
#ifndef GLOBALDEFINE_H
#define GLOBALDEFINE_H

#include <QObject>

enum E_MSG_ID:int
{
    E_MSG_ID_FILEINFO = 0x1000,     //文件信息
    E_MSG_ID_NEXTPAGE,              //下一页
    E_MSG_ID_PREPAGE,               //上一页
    E_MSG_ID_TURNPAGE,              //跳转     帧头,加页数即可
    E_MSG_ID_CURPAGE,               //当前页信息,用于刷新吧
};

#pragma pack(1)

struct stNetFrameHead
{
    int nMsgID;                 //消息ID
    int nLength;                //消息长度,不包括帧头
    int nCount;                 //总包数
    int nSeqNum;                //包序号
    char cReserve[32];          //预留,待后续添加字段后,不用频繁更新
    stNetFrameHead()
    {
        memset(this,0,sizeof(stNetFrameHead));
    }
};

//服务的返回的格式stNetFrameHead+stFileInfo
struct stFileInfo
{
    char    cFileName[50];          //文件名
    int     nTotalPages;            //文件总页数
    stFileInfo()
    {
        memset(this,0,sizeof(stFileInfo));
    }
};

//cur next pre turn数据格式为 服务端返回的格式
//stNetFrameHead+curPage+pdfImageData


//客户端请求为:stNetFrameHead
#pragma pack()

#endif // GLOBALDEFINE_H

pdf操作类代码文件
头文件
#ifndef PDFOPERATOR_H
#define PDFOPERATOR_H

#include <QPixmap>
class QPdfium;

class CPdfOperator
{
public:
    CPdfOperator(const QString &strFilePath);
    ~CPdfOperator();

    /**
     * @brief getFileInfo 获取pdf文件信息
     * @param strFileNmae   文件名
     * @param nTotalPages   总页数
     * @return 获取结果
     */
    bool getFileInfo(QString& strFileNmae,int& nTotalPages);

    /**
     * @brief getPrePage 获取前一页pdf内容
     * @param pageData pdf内容
     * @param cruPage 前端展示的当前页号
     * @return 获取结果
     */
    bool getPrePage(QByteArray& pageData,int& cruPage);

    /**
     * @brief getNextPage 获取下一页pdf内容
     * @param pageData pdf内容
     * @param cruPage 前端展示的当前页号
     * @return 获取结果
     */
    bool getNextPage(QByteArray& pageData,int& cruPage);

    /**
     * @brief getTurn 获取跳转到指定页的pdf内容
     * @param turnPage 跳转的页码
     * @param pageData pdf内容
     * @param cruPage 前端展示的当前页号
     * @return 获取结果
     */
    bool getTurn(const int& turnPage,QByteArray& pageData,int& cruPage);

    /**
     * @brief getCurPageData 获取当前页的pdf内容
     * @param pageData pdf内容
     * @param cruPage 前端展示的当前页号
     * @return 获取结果
     */
    bool getCurPageData(QByteArray& pageData,int& cruPage);

private:

    QPdfium         *m_pPdfMgr;     //开源库pdf管理对象指针
    int             m_nTotalPages;  //总页数
    int             m_nCurPage;     //当前页码从0开始
    QString         m_strFilePath;  //pdf文件路径
};

#endif // PDFOPERATOR_H

cpp文件
#include "PdfOperator.h"
#include "qpdfium.h"
#include <QFileInfo>
#include <QDataStream>

#define DATASTREAM 1

CPdfOperator::CPdfOperator(const QString &strFilePath)
{
    m_strFilePath = strFilePath;
    m_pPdfMgr = new QPdfium;

    m_nTotalPages = 0;
    m_nCurPage = 0;

    m_pPdfMgr->loadFile(strFilePath); //path为pdf路径
    if(!m_pPdfMgr->isValid())
    {
        return;
    }
    m_nTotalPages = m_pPdfMgr->pageCount();
    if(m_nTotalPages<=0)
    {
        m_pPdfMgr->loadFile("");
        return;
    }
}

CPdfOperator::~CPdfOperator()
{
    if(m_pPdfMgr)
    {
        delete m_pPdfMgr;
        m_pPdfMgr = nullptr;
    }
}

bool CPdfOperator::getFileInfo(QString &strFileNmae, int &nTotalPages)
{
    if(!m_pPdfMgr->isValid())
    {
        return false;
    }

    QFileInfo tmpInfo(m_strFilePath);
    strFileNmae = tmpInfo.fileName();
    nTotalPages = m_nTotalPages;
    return true;
}

bool CPdfOperator::getPrePage(QByteArray &pageData, int &cruPage)
{
    if(!m_pPdfMgr->isValid())
    {
        return false;
    }
    if(m_nCurPage == 0)//表示已经是第一页了,无法获取前一页
    {
        return false;
    }
    QPdfiumPage page =  m_pPdfMgr->page(m_nCurPage - 1);  //表示获取第n页,这里表示不是第一页,那么则获取当前页的前一页
    QImage image = page.image(1.0);  //将pdf转化为Qimage,后面的1.0代表显示比例

    m_nCurPage-=1;

#if DATASTREAM
    QDataStream ds(&pageData,QIODevice::WriteOnly);
    //将图片读入array,方便发送
    ds<<image;
#else
    pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif
    cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1

    return true;
}

bool CPdfOperator::getNextPage(QByteArray &pageData, int &cruPage)
{
    if(!m_pPdfMgr->isValid())
    {
        return false;
    }
    if(m_nCurPage+1 == m_nTotalPages)//表示已经最后一页了,无法获取下一页
    {
        return false;
    }
    QPdfiumPage page =  m_pPdfMgr->page(m_nCurPage + 1);  //读取每一页pdf
    QImage image = page .image(1.0);  //将pdf转化为Qimage,后面的1.0代表显示比例

    m_nCurPage+=1;


#if DATASTREAM
    QDataStream ds(&pageData,QIODevice::WriteOnly);
    //将图片读入array,方便发送
    ds<<image;
#else
    pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif

    cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1

    return true;
}

bool CPdfOperator::getTurn(const int& nTurnPage,QByteArray &pageData, int &cruPage)
{
    if(!m_pPdfMgr->isValid())
    {
        return false;
    }

    if((nTurnPage > m_nTotalPages) || (nTurnPage <=0))
    {
        return false;
    }


    QPdfiumPage page =  m_pPdfMgr->page(nTurnPage-1);  //读取每一页pdf
    QImage image = page .image(1.0);  //将pdf转化为Qimage,后面的1.0代表显示比例
    m_nCurPage = nTurnPage -1;


#if DATASTREAM
    QDataStream ds(&pageData,QIODevice::WriteOnly);
    //将图片读入array,方便发送
    ds<<image;
#else
    pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif


    cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1

    return true;
}

bool CPdfOperator::getCurPageData(QByteArray &pageData, int &cruPage)
{
    if(!m_pPdfMgr->isValid())
    {
        return false;
    }
    QPdfiumPage page =  m_pPdfMgr->page(m_nCurPage);  //读取每一页pdf
    QImage image = page .image(1.0);  //将pdf转化为Qimage,后面的1.0代表显示比例


#if DATASTREAM
    QDataStream ds(&pageData,QIODevice::WriteOnly);
    //将图片读入array,方便发送
    ds<<image;
#else
    pageData = QByteArray((char*)image.bits(),image.byteCount());
#endif

    cruPage = m_nCurPage+1;//给用户看的,下标从1开始,那么为当前页+1
    return true;
}

网络udp操作类代码文件
头文件
#ifndef UDPOPERATOR_H
#define UDPOPERATOR_H

#include <QUdpSocket>
#include <QFuture>
#include <QSemaphore>
#include <QQueue>
#include <QPixmap>



class CPdfOperator;

class CUdpOperator:public QObject
{
    Q_OBJECT
public:
    CUdpOperator();

    ~CUdpOperator();

    /**
     * @brief setPdfFile 设置pdf文件路径
     * @param strFilePath 文件路径
     */
    void setPdfFile(const QString& strFilePath);

private slots:
    /**
     * @brief recvNetData 接收udp数据的槽函数
     */
    void recvNetData();


private:
    /**
     * @brief onCheckThread 处理udp数据的线程函数
     */
    void onCheckThread();

    /**
     * @brief parseNetWork 解析网络数据函数
     * @param netData 网络数据
     */
    void parseNetWork(const QByteArray& netData);

    /**
     * @brief init 初始化函数
     */
    void init();

    /**
     * @brief parseFileInfo 解析收到获取文件信息的请求
     */
    void parseFileInfo();

    /**
     * @brief parsePages 解析获取某一页pdf内容的请求
     * @param nType 请求类型【前一页、后一页、当前页】
     */
    void parsePages(const int& nType);

    /**
     * @brief parseTurnPages 解析获取跳转页请求
     * @param nTurnPage 跳转页码
     */
    void parseTurnPages(const int& nTurnPage);

    /**
     * @brief sendNetData 发送网络数据
     * @param nMsgId 消息ID
     * @param netData 消息数据
     */
    void sendNetData(const int& nMsgId,const QByteArray& netData);
private:

    QUdpSocket              m_udpSocket;                //和前端通信的套接字对象

    QFuture<void>           m_future;                   //处理数据线程
    QSemaphore              m_semaphoreData;            //处理数据信号量

    QQueue<QByteArray>      m_queueNetData;             //网络数据队列
    QMutex                  m_mutexQueue;               //队列锁
    bool                    m_bExitThread = false;      //是否退出解析线程


    CPdfOperator         *m_pPdfMgr = nullptr;          //pdf操作对象指针
};

#endif // UDPOPERATOR_H

cpp文件
#include "UdpOperator.h"
#include "GlobalDefine.h"
#include <QImage>
#include <QHostAddress>
#include "PdfOperator.h"
#include <QtConcurrent>
#include <QThread>

#define MAXNETPAGE      40960
CUdpOperator::CUdpOperator()
{
    init();
}

CUdpOperator:: ~CUdpOperator()
{
    m_udpSocket.abort();
    if(m_pPdfMgr)
    {
        delete m_pPdfMgr;
        m_pPdfMgr = nullptr;
    }
    if(m_future.isRunning())
    {
        m_bExitThread = true;
        m_semaphoreData.release();
        m_future.waitForFinished();
    }
}


void CUdpOperator::setPdfFile(const QString &strFilePath)
{
    if(m_pPdfMgr == nullptr)
    {
        m_pPdfMgr = new CPdfOperator(strFilePath);
    }
    else
    {
        delete m_pPdfMgr;
        m_pPdfMgr = nullptr;

        m_pPdfMgr = new CPdfOperator(strFilePath);
    }
}


void CUdpOperator::init()
{
    m_future = QtConcurrent::run(this,&CUdpOperator::onCheckThread);
    m_udpSocket.bind(QHostAddress::AnyIPv4,10010);

    connect(&m_udpSocket,SIGNAL(readyRead()),this,SLOT(recvNetData()));
}

void CUdpOperator::parseFileInfo()
{
    if(!m_pPdfMgr)
    {
        return;
    }
    QString strFileName;int nTotalPages = 0;
    if(!m_pPdfMgr->getFileInfo(strFileName,nTotalPages))
    {
        return;
    }
    stFileInfo tmpInfo;
    memcpy(tmpInfo.cFileName,strFileName.toUtf8().data(),strFileName.toUtf8().length());
    tmpInfo.nTotalPages = nTotalPages;

    QByteArray tmpArray;
    tmpArray.append((char*)&tmpInfo,sizeof(tmpInfo));

    sendNetData(E_MSG_ID_FILEINFO,tmpArray);


    QByteArray imageData;int curPage = 0;
    if(!m_pPdfMgr->getCurPageData(imageData,curPage))
    {
        return;
    }


    QByteArray tmpArraySendFirstPage;
    tmpArraySendFirstPage.append((char*)&curPage,sizeof(curPage));
    tmpArraySendFirstPage.append(imageData);

    sendNetData(E_MSG_ID_CURPAGE,tmpArraySendFirstPage);
}

void CUdpOperator::parsePages(const int& nType)
{
    if(!m_pPdfMgr)
    {
        return;
    }
    QByteArray imageData;int curPage = 0;

    switch (nType)
    {
    case E_MSG_ID_NEXTPAGE:
        if(!m_pPdfMgr->getNextPage(imageData,curPage))
        {
            return;
        }
        break;
    case E_MSG_ID_PREPAGE:
        if(!m_pPdfMgr->getPrePage(imageData,curPage))
        {
            return;
        }
        break;
    default:
        return;
        break;
    }

    int nLength = imageData.length();
    if(nLength <=0)
    {
        return;
    }


    QByteArray tmpArray;
    tmpArray.append((char*)&curPage,sizeof(curPage));
    tmpArray.append(imageData);

    sendNetData(nType,tmpArray);
}

void CUdpOperator::parseTurnPages(const int &nType)
{
    if(!m_pPdfMgr)
    {
        return;
    }
    QByteArray imageData;int curPage = 0;
    if(!m_pPdfMgr->getTurn(nType,imageData,curPage))
    {
        return;
    }
    int nLength = imageData.length();
    if(nLength <=0)
    {
        return;
    }

    QByteArray tmpArray;
    tmpArray.append((char*)&curPage,sizeof(curPage));
    tmpArray.append(imageData);

    sendNetData(E_MSG_ID_TURNPAGE,tmpArray);
}

void CUdpOperator::sendNetData(const int &nMsgId, const QByteArray &netData)
{
    //每一包最大字节数为MAXNETPAGE,包括帧头的长度,udp最大发送字节数不能超过65535
    int nDataLength = netData.length();
    int nFrameHeadLen = sizeof(stNetFrameHead);
    int nPacketAlivable = MAXNETPAGE - nFrameHeadLen;
    if(nDataLength + nFrameHeadLen > MAXNETPAGE)
    {
        int nTotalLength = nDataLength;

        int nPackCount = nTotalLength % nPacketAlivable?nTotalLength/nPacketAlivable+1:nTotalLength/nPacketAlivable;

        for(int i = 0;i< nPackCount;i++)
        {
            if(i != nPackCount -1)
            {
                stNetFrameHead tmpHead;
                tmpHead.nCount = nPackCount;
                tmpHead.nLength = MAXNETPAGE - nFrameHeadLen;
                tmpHead.nMsgID = nMsgId;
                tmpHead.nSeqNum = i;
                QByteArray tmpArray;
                tmpArray.append((char*)&tmpHead,nFrameHeadLen);
                tmpArray.append(netData.data()+(MAXNETPAGE - nFrameHeadLen)*i,MAXNETPAGE - nFrameHeadLen);
                m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
            }
            else    //最后一包
            {
                stNetFrameHead tmpHead;
                tmpHead.nCount = nPackCount;
                tmpHead.nLength = nDataLength - i*(MAXNETPAGE - nFrameHeadLen);
                tmpHead.nMsgID = nMsgId;
                tmpHead.nSeqNum = i;
                QByteArray tmpArray;
                tmpArray.append((char*)&tmpHead,nFrameHeadLen);
                tmpArray.append(netData.data()+(MAXNETPAGE - nFrameHeadLen)*i,nDataLength - i*(MAXNETPAGE - nFrameHeadLen));
                m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
            }
            QThread::msleep(100);//发送太快会失败//真实代码需要优化,用线程发送,或者用c++原始UDP套接字
        }
    }
    else
    {
        stNetFrameHead tmpHead;
        tmpHead.nCount = 1;
        tmpHead.nLength = netData.length();
        tmpHead.nMsgID = nMsgId;
        tmpHead.nSeqNum = 0;
        QByteArray tmpArray;
        tmpArray.append((char*)&tmpHead,nFrameHeadLen);
        tmpArray.append(netData);

        m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10086);
    }
}


void CUdpOperator::recvNetData()
{
    QByteArray array;
    QHostAddress address;
    quint16 port;
    array.resize(m_udpSocket.bytesAvailable());//根据可读数据来设置空间大小
    m_udpSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据

    m_mutexQueue.lock();
    m_queueNetData.enqueue(array);
    m_mutexQueue.unlock();
    m_semaphoreData.release();
}

void CUdpOperator::onCheckThread()
{
    while(true)
    {
        if(m_bExitThread)
        {
            break;
        }
        m_semaphoreData.acquire();
        if(m_bExitThread)
        {
            break;
        }
        m_mutexQueue.lock();
        QByteArray array = m_queueNetData.dequeue();
        m_mutexQueue.unlock();
        parseNetWork(array);
    }
}

void CUdpOperator::parseNetWork(const QByteArray& netData)
{
    int nFrameLength = sizeof(stNetFrameHead);
    if(netData.length() < nFrameLength)
    {
        return;
    }
    stNetFrameHead tmpHead;
    memcpy(&tmpHead,netData.constData(),nFrameLength);

    switch (tmpHead.nMsgID)
    {
    case E_MSG_ID_FILEINFO:
        parseFileInfo();
        break;
    case E_MSG_ID_NEXTPAGE:
    case E_MSG_ID_PREPAGE:
        parsePages(tmpHead.nMsgID);
        break;
    case E_MSG_ID_TURNPAGE:
    {
        int nTurnPage = 0;
        memcpy(&nTurnPage, netData.data()+ nFrameLength,netData.length() - nFrameLength);
        parseTurnPages(nTurnPage);
    }
        break;
    default:
        return;
        break;
    }

}

界面文件就一个函数
QString strFileName = QFileDialog::getOpenFileName(this,"open pdf","/home"
                                                       ,"*.pdf");
    if(strFileName.isEmpty())
    {
        return;
    }
    m_udpoperator.setPdfFile(strFileName);

这里用宏定义区分开了代码,前后端的界面是一样的,我懒,就copy了一份代码作为服务端,宏定义为1时,就是本地操作pdf的演示代码,详情下载源码了解。

客户端准备

客户端点击打开文件,可以获取服务端pdf文件信息,文件名和总页数,获取成功后,就可以进行操作了,上一页,下一页,以及跳转,进行文件的缩放【暂时还有bug,后续解决完善】

全局定义头文件

同服务端一样

udp操作类
头文件
#ifndef UDPOPERATOR_H
#define UDPOPERATOR_H

#include <QUdpSocket>
#include <QFuture>
#include <QSemaphore>
#include <QQueue>
#include <QPixmap>
#include <QMap>

enum E_MSG_ID;
class CUdpOperator:public QObject
{
    Q_OBJECT
public:
    CUdpOperator();

    ~CUdpOperator();

    /**
     * @brief PdfOpertorType pdf亲求类型
     * @param eType 请求类型
     * @param nTurnPage 如果为跳转请求,则表示跳转页数,其他请求不用
     */
    void PdfOpertorType(const E_MSG_ID eType,const int& nTurnPage = 0);

signals:

    /**
     * @brief signalFileInfo pdf文件信息请求结果
     * @param strFileNmae 文件名
     * @param nTotalPages 文件页数
     */
    void signalFileInfo(const QString& strFileNmae,const int& nTotalPages);

    /**
     * @brief signalCurPageInfo pdf内容请求结果
     * @param pixMap pdf内容
     * @param nCurPage 当前显示页码
     */
    void signalCurPageInfo(const QPixmap& pixMap,const int& nCurPage);

private slots:
    /**
     * @brief recvNetData 结束服务端网络数据的槽函数
     */
    void recvNetData();


private:
    /**
     * @brief onCheckThread 处理服务端数据的线程函数
     */
    void onCheckThread();

    /**
     * @brief parseNetWork 解析网络数据
     * @param netData 网络数据
     */
    void parseNetWork(const QByteArray& netData);

    /**
     * @brief init 初始化操作
     */
    void init();

    /**
     * @brief parseFileInfo 解析请求文件信息结果
     * @param nLength 数据长度
     * @param pData 数据起始地址指针
     */
    void parseFileInfo(const int& nLength,const char* pData);

    /**
     * @brief parsePages 解析请求获取pdf页码内容结果
     * @param nLength 数据长度
     * @param pData 数据起始地址指针
     */
    void parsePages(const int& nLength,const char* pData);
private:

    QUdpSocket              m_udpSocket;                //和服务端通信的套接字对象

    QFuture<void>           m_future;                   //处理数据线程
    QSemaphore              m_semaphoreData;            //处理数据信号量

    QQueue<QByteArray>      m_queueNetData;             //数据队列
    QMutex                  m_mutexQueue;               //队列锁
    bool                    m_bExitThread = false;      //退出处理线程标志

    QMap<int ,QMap<int,QByteArray>> m_mapNetData;       //组包使用的容器,分别表示消息id,消息序号,消息内容
};

#endif // UDPOPERATOR_H

cpp文件
#include "UdpOperator.h"
#include "GlobalDefine.h"
#include <QImage>
#include <QHostAddress>
#include <QtConcurrent>

CUdpOperator::CUdpOperator()
{
    init();
}

CUdpOperator::~CUdpOperator()
{
    m_udpSocket.abort();
    if(m_future.isRunning())
    {
        m_bExitThread = true;
        m_semaphoreData.release();
        m_future.waitForFinished();
    }
}


void CUdpOperator::PdfOpertorType(const E_MSG_ID eType, const int &nTurnPage)
{
    stNetFrameHead tmpHead;
    tmpHead.nMsgID = eType;
    tmpHead.nCount = 1;
    tmpHead.nLength = 0;
    tmpHead.nSeqNum = 0;

    if(eType == E_MSG_ID_TURNPAGE)
    {
        QByteArray tmpArray;
        tmpArray.append((char*)&tmpHead,sizeof(tmpHead));
        tmpArray.append((char*)&nTurnPage,sizeof(nTurnPage));
        m_udpSocket.writeDatagram(tmpArray,QHostAddress("127.0.0.1"),10010);
    }
    else
    {
        m_udpSocket.writeDatagram((char*)&tmpHead,sizeof(tmpHead),QHostAddress("127.0.0.1"),10010);
    }

}

void CUdpOperator::init()
{
    m_future = QtConcurrent::run(this,&CUdpOperator::onCheckThread);
    m_udpSocket.bind(QHostAddress::AnyIPv4,10086);

    connect(&m_udpSocket,SIGNAL(readyRead()),this,SLOT(recvNetData()));
}

void CUdpOperator::parseFileInfo(const int &nLength, const char *pData)
{
    int nStInfoLength = sizeof(stFileInfo);
    if(pData == NULL || nLength != nStInfoLength)
    {
        return;
    }
    stFileInfo tmpInfo;
    memcpy(&tmpInfo,pData,nStInfoLength);

    emit signalFileInfo(tmpInfo.cFileName,tmpInfo.nTotalPages);
}

void CUdpOperator::parsePages(const int &nLength, const char *pData)
{
    int nCurLength = sizeof(int);
    if(pData == NULL || nLength <= nCurLength)
    {
        return;
    }
    int curPage;
    memcpy(&curPage,pData,nCurLength);

    // QImage image = QImage::fromData();
    QByteArray msg =  QByteArray(pData+nCurLength,nLength-nCurLength);
    QImage img;
    QDataStream ds(&msg,QIODevice::ReadOnly);
    ds>>img;

    emit signalCurPageInfo(QPixmap::fromImage(img),curPage);
}


void CUdpOperator::recvNetData()
{
    QByteArray array;
    QHostAddress address;
    quint16 port;
    array.resize(m_udpSocket.bytesAvailable());//根据可读数据来设置空间大小
    m_udpSocket.readDatagram(array.data(),array.size(),&address,&port); //读取数据

    m_mutexQueue.lock();
    m_queueNetData.enqueue(array);
    m_mutexQueue.unlock();
    m_semaphoreData.release();
}

void CUdpOperator::onCheckThread()
{
    while(true)
    {
        if(m_bExitThread)
        {
            break;
        }
        m_semaphoreData.acquire();
        if(m_bExitThread)
        {
            break;
        }
        m_mutexQueue.lock();
        QByteArray array = m_queueNetData.dequeue();
        m_mutexQueue.unlock();
        parseNetWork(array);
    }
}

void CUdpOperator::parseNetWork(const QByteArray& netData)
{
    int nFrameLength = sizeof(stNetFrameHead);
    if(netData.length() < nFrameLength)
    {
        return;
    }
    stNetFrameHead tmpHead;
    memcpy(&tmpHead,netData.constData(),nFrameLength);

    if(tmpHead.nCount != 1)
    {
        if(m_mapNetData.contains(tmpHead.nMsgID))
        {
            m_mapNetData[tmpHead.nMsgID][tmpHead.nSeqNum] = QByteArray(netData.data()+nFrameLength,netData.length() - nFrameLength);
            if(m_mapNetData[tmpHead.nMsgID].size() == tmpHead.nCount)//组包完成 todo这里没考虑超时,丢包的情况,真实项目代码需要考虑,可以参考博客https://blog.csdn/qq_44771391/article/details/132258308?spm=1001.2014.3001.5502
            {
                QList<QByteArray> tmpLst = m_mapNetData[tmpHead.nMsgID].values();
                QByteArray tmpArray;
                for(int i = 0;i<tmpLst.size();i++)
                {
                    tmpArray.append(tmpLst.at(i));
                }
                parsePages(tmpArray.length(),tmpArray.data());//这里消息没做区分,是因为文件信息不会组包,不会超过65535
                m_mapNetData.remove(tmpHead.nMsgID);
            }
        }
        else
        {
            m_mapNetData[tmpHead.nMsgID][tmpHead.nSeqNum] = QByteArray(netData.data()+nFrameLength,netData.length() - nFrameLength);
        }
    }
    else
    {
        switch (tmpHead.nMsgID)
        {
        case E_MSG_ID_FILEINFO:
            parseFileInfo(netData.length() - nFrameLength,netData.data()+nFrameLength);
            break;
        case E_MSG_ID_NEXTPAGE:
        case E_MSG_ID_PREPAGE:
        case E_MSG_ID_TURNPAGE:
        case E_MSG_ID_CURPAGE:
            parsePages(netData.length() - nFrameLength,netData.data()+nFrameLength);
            break;
        default:
            return;
            break;
        }
    }



}

界面文件
头文件
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPixmap>

class QPdfium;

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class CUdpOperator;

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    /**
     * @brief on_btn_openFile_clicked 发送文件请求
     */
    void on_btn_openFile_clicked();

    /**
     * @brief on_btn_prePage_clicked 发送前一页请求
     */
    void on_btn_prePage_clicked();

    /**
     * @brief on_btn_nextPage_clicked 发送下一页请求
     */
    void on_btn_nextPage_clicked();

    /**
     * @brief on_btn_turn_clicked 发送跳转请求
     */
    void on_btn_turn_clicked();

    /**
     * @brief on_FileInfo 得到文件内容结果
     * @param strFileNmae 文件名
     * @param nTotalPages 总页数
     */
    void on_FileInfo(const QString& strFileNmae,const int& nTotalPages);

    /**
     * @brief on_CurPageInfo 当前展示页结果
     * @param pixMap pdf内容
     * @param nCurPage 当前页码
     */
    void on_CurPageInfo(const QPixmap& pixMap,const int& nCurPage);

protected:

    //事件过滤器,用于Pdf内容缩放使用,目前不完善待后续完善
    bool eventFilter(QObject *watched, QEvent *event) override;
private:
    Ui::Widget *ui;

    CUdpOperator*       m_pUdpOpertor = nullptr;    //udp操作类
    qreal               m_zoomFactor;           //缩放系数
    QPixmap             m_curPdf;               //当前Pdf内容
};
#endif // WIDGET_H

cpp文件
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QWheelEvent>

#include "UdpOperator.h"
#include "GlobalDefine.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    m_zoomFactor = 1;
    ui->label_showPdf->installEventFilter(this);

    m_pUdpOpertor = new CUdpOperator;
    connect(m_pUdpOpertor,SIGNAL(signalFileInfo(const QString& ,const int& )),this,SLOT(on_FileInfo(const QString& ,const int& )));
    connect(m_pUdpOpertor,SIGNAL(signalCurPageInfo(const QPixmap& ,const int& )),this,SLOT(on_CurPageInfo(const QPixmap& ,const int& )));

    setWindowTitle("Client");
}

Widget::~Widget()
{
    if(m_pUdpOpertor)
    {
        delete m_pUdpOpertor;
        m_pUdpOpertor = nullptr;
    }
    delete ui;
}

void Widget::on_btn_openFile_clicked()
{
    m_pUdpOpertor->PdfOpertorType(E_MSG_ID_FILEINFO);
}


void Widget::on_btn_prePage_clicked()
{
    m_pUdpOpertor->PdfOpertorType(E_MSG_ID_PREPAGE);
}


void Widget::on_btn_nextPage_clicked()
{
    m_pUdpOpertor->PdfOpertorType(E_MSG_ID_NEXTPAGE);
}


void Widget::on_btn_turn_clicked()
{
    m_pUdpOpertor->PdfOpertorType(E_MSG_ID_TURNPAGE,ui->spinBox_turnPage->value());
}

void Widget::on_FileInfo(const QString &strFileNmae, const int &nTotalPages)
{
    ui->label_FileName->setText(strFileNmae);
    ui->spinBox_totalPage->setValue(nTotalPages);

}

void Widget::on_CurPageInfo(const QPixmap &pixMap, const int &nCurPage)
{
    m_curPdf = pixMap;
    ui->label_showPdf->setPixmap(m_curPdf);
    ui->spinBox_curPage->setValue(nCurPage);
}

bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    if(event->type() != QEvent::Wheel)
    {
        return  QWidget::eventFilter(watched,event);
    }

    if(watched == ui->label_showPdf)//判断是否是label_showPdf委托
    {
        QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);

        //获取滚轮角步数
        int value = wheelEvent->angleDelta().y();

        qreal deltzZoom = 0.1;//缩放因子增量


        if(value > 0) //放大
        {
            m_zoomFactor += deltzZoom;
        }
        else //缩小
        {
            m_zoomFactor -= deltzZoom;
        }
        if(m_zoomFactor<0.1)
        {
            m_zoomFactor = 0.1;
        }
        // ui->label_showPdf->setPixmap(m_curPdf.scaled(m_zoomFactor*m_curPdf.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
        ui->label_showPdf->setPixmap(m_curPdf.scaled(m_zoomFactor*ui->label_showPdf->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
        update();
        return true;//阻止传递到对象
    }
    else
    {
        return  QWidget::eventFilter(watched,event);//继续传递到对象
    }
}


结果展示

其他

1.基本完成了pdf内容的浏览,pdf在网络中主要以图片的形式进行发送,pdf质量取决于图片的质量,代码中有两种方式发送QImage,一种发送原始数据,一种是QDataStream序列化的方式,用宏定义区分开了,还有其他方式没有进行尝试,待后续完善。
2.该程序还不算完善,pdf的缩放不是很好,pdf质量可能是我没用原始数据的原因,也不是很好,待后续完善。
3.服务端程序可以进行本地pdf展示,将宏定义值在0和1切换即可。
4.资源文件里面包含第三方pdf操作库的源码,在参考博客里面和源码里面可以看到用法,另外高版本Qt里面有自带pdf操作库,不知道是不是这个。如果版本较低的Qt想简单操作pdf可以使用这个库,比较里面的变量都是Qt相关的兼容度比较好。

更新【20241013】

1.更新是缩放界面,前后端均更新;后端在本地模式(LOCAL_PDF宏开关0 1切换)的时候可以查看,并且添加缩放功能。

后端界面

2.缩放解决方案不是很好,将就能用,但是前端显示速度还待提高,后续是性能方面的提高,以及缩放看是否有更好的方案,目前是将pdf内容直接提取较大放大倍速发送到前端,前端通过还原比例,这样画质不会太离谱,也算一种方式,后续看是否有更好的方案再更新,比如用矢量图。

后端核心代码

QImage image = page.image(INISIZE);  //将pdf转化为Qimage,后面的1.0代表显示比例,这里INISIZE初始化为3.0

前端核心代码

QSvgRenderer tmpSvg;
 tmpSvg.load(m_curSvg);

 QPixmap pixMap(tmpSvg.defaultSize()/3*m_zoomFactor);//这里的3对应前面的INISIZE宏定义,后期优化可以在网络阶段进行配置
 pixMap.fill(Qt::transparent);
 QPainter painter1(&pixMap);
 painter1.setRenderHints(QPainter::Antialiasing);
 tmpSvg.render(&painter1);

 ui->label_showPdf->resize(pixMap.size());
 ui->label_showPdf->setPixmap(pixMap);

这样操作后,前端的画质基本和后端持平,但是最大放大倍数理论应该和INISIZE一样,否则也会画质模糊,前后的INISIZE一样,则能保持画质质量。
另:上面的svg相关,发现不太行,应该还是和原始画质有关,QImage image = page.image(INISIZE);这里相当于是原始数据,如果INISIZE是1.0,转为Svg,发送到前端,前端放大还是会模糊,但是我如果INISIZE为3.0,到前端在缩小放大画质就还可以【不太了解图像处理这一一块,后续学习一下】。

本文标签: 文件QTPDF