侧边栏壁纸
博主头像
三味的小站 博主等级

世界上没有偶然,有的只是必然的结果。

  • 累计撰写 63 篇文章
  • 累计创建 14 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
Qt

poppler-qt5编译

三味线
2024-09-25 / 0 评论 / 0 点赞 / 75 阅读 / 0 字

项目需要展示PDF,环境为VS 2015 + Qt 5.6.3,所以新的库无法使用,另一个库qpdf虽然更易使用,但是需要Qt5.9以上,多番查找,未发现更好的库,只能自行编译poppler。

编译过程参考了以下两篇文章:

https://blog.csdn.net/u012509849/article/details/103121755

https://blog.csdn.net/pascadores/article/details/137591143

1. 下载poppler

官网:https://poppler.freedesktop.org/

测试了多个版本,发现只有0.69能编译成功

2. 下载cmake

官网:https://cmake.org/download/

这里使用的版本是:cmake-3.29.2-windows-i386

最开始用的其他版本,但是vcpkg会报错,按其提示使用了此版本

3. 下载vcpkg

https://github.com/microsoft/vcpkg

下载之后放到合适位置,命令行执行:bootstrap-vcpkg.bat

我这里只需要编译32位库,且只使用VS 2015,所以做以下两个更改:

  • 添加环境变量:VCPKG_DEFAULT_TRIPLET = x86-windows

  • 修改vcpkg目录中的文件:triplets/x86-windows.cmake,在最后一行添加:set(VCPKG_PLATFORM_TOOLSET v140)

命令行执行vcpkg install $pkg_name 安装依赖库

大概安装了以下包:brotli、bzip2、cairo、curl、devil、dirent、egl-registry、expat、freeglut、freetype、libiconv、libjpeg-turbo、liblzma、libpng、openexr、opengl、openjpeg、tiff、zlib

4. 编译poppler-data

需要先编译poppler-data,同样在官网下载

双击之前安装的cmake-gui.exe,源码选择poppler-data目录,新建一个目录用于编译

依次点击Configure、Generate、Open Project,在VS中进行编译

将编译好的share文件夹下的文件拷贝到vcpkg的目录中:installed/x86-windows/share/

5. 编译poppler

修改poppler-0.69.0下的CMakeLists.txt,在前面配置vcpkg目录,修改后的前面几行如下:

cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
set(CMAKE_TOOLCHAIN_FILE "D:/Programs/vcpkg/scripts/buildsystems/vcpkg.cmake")
project(poppler)

set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)

include(PopplerDefaults)
include(PopplerMacros)
include(MacroOptionalFindPackage)
find_package(PkgConfig)
include(MacroEnsureVersion)
include(MacroBoolTo01)

第一次会报错没有test目录,按提示用git下载,放到poppler-0.69.0同一级目录

根据cmake提示调整配置,取消勾选不需要的,目录不对的修改下

最后在VS中选择poppler-qt进行编译,注意选择Debug或Release,完成后可在目录qt5/src/Debugqt5/src/Release找到编译好的文件

头文件直接使用poppler-0.69.0/qt5/src

整个编译过程中可能会遇到其他一些问题,通过搜索和询问AI一般都能解决。

6. 测试代码

使用QGraphicsView展示,由于大文件加载很慢,用QTimer依次加载

首先在pro中添加头文件和库配置:

INCLUDEPATH += $$PWD/poppler
LIBS += -lpoppler-qt5

将库拷贝到编译目录

新建的类如下:

pdfviewer.h

#ifndef PDFVIEWER_H
#define PDFVIEWER_H

#include <QGraphicsView>
#include <QPixmap>
#include <QWheelEvent>
#include "poppler-qt5.h"

class PDFViewer : public QGraphicsView {
    Q_OBJECT

public:
    explicit PDFViewer(QWidget *parent = nullptr);
    ~PDFViewer();

    bool loadFile(const QString &iPath);
    void clearData();

signals:
    void pixChanged();
public slots:
    void timeOutSlot();
    void pixChangedSlot();
protected:
    void wheelEvent(QWheelEvent *event) override;
private:
    QList<QPixmap> mPixmapList;
    QGraphicsScene *mScene = nullptr;
    Poppler::Document *mDocument = nullptr;
    qreal mYOffset = 0;
    qreal mSceneWidth = 0;
    int mLoadedCount = 3;
    QTimer *mTimer = nullptr;
};

#endif // PDFVIEWER_H

pdfviewer.cpp

#include "pdfviewer.h"
#include <QTimer>
#include <QImage>
#include <QScrollBar>
#include <QtConcurrent>
#include <QFileInfo>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>


PDFViewer::PDFViewer(QWidget *iParent)
    : QGraphicsView(iParent)
{
    mScene = new QGraphicsScene(this);
    setScene(mScene);
    // 启用拖动功能
    setDragMode(QGraphicsView::ScrollHandDrag);
    // 设置视图属性
    setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing|QPainter::SmoothPixmapTransform);
    setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
    setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    setResizeAnchor(QGraphicsView::AnchorUnderMouse);

    mTimer = new QTimer(this);
    mTimer->setSingleShot(true);
    mTimer->setInterval(10);
    connect(mTimer, SIGNAL(timeout()), this, SLOT(timeOutSlot()));
    connect(this, SIGNAL(pixChanged()), this, SLOT(pixChangedSlot()), Qt::QueuedConnection);
}

PDFViewer::~PDFViewer()
{
    mTimer->stop();
    if (mDocument) {
        delete mDocument;
        mDocument = nullptr;
    }
}

bool PDFViewer::loadFile(const QString &iPath)
{
    clearData();
    // 加载PDF文件
    mDocument = Poppler::Document::load(iPath);
    if (!mDocument || mDocument->isLocked()) {
        qDebug()<<"load pdf file failed!";
        return false;
    }
    mDocument->setRenderHint(Poppler::Document::Antialiasing, true);
    mDocument->setRenderHint(Poppler::Document::TextAntialiasing, true);

     // 用于垂直排列页面的偏移量
    const double resX = physicalDpiX() * 2.0;
    const double resY = physicalDpiY() * 2.0;
    for (int i = 0; i < mLoadedCount; ++i) {
        Poppler::Page *page = mDocument->page(i);
        if (page) {
            QImage image = page->renderToImage(resX, resY);
            if (!image.isNull()) {
                QPixmap pixmap = QPixmap::fromImage(image);
                QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
                mScene->addItem(item);

                // 设置页面位置
                item->setPos(0, mYOffset);
                mYOffset += pixmap.height(); // 更新偏移量
                if (pixmap.width() > mSceneWidth) {
                    mSceneWidth = pixmap.width();
                }
            }
        }
    }
    mScene->setSceneRect(0, 0, mSceneWidth, mYOffset);
    resetTransform();
    centerOn(0, 0);
    mTimer->start();
    return true;
}

void PDFViewer::clearData()
{
    mTimer->stop();
    if (mDocument) {
        delete mDocument;
        mDocument = nullptr;
    }
    mScene->clear();
    mScene->setSceneRect(0,0,0,0);
    fitInView(mScene->sceneRect(), Qt::KeepAspectRatio);
    resetTransform();
    mYOffset = 0;
    mSceneWidth = 0;
    mLoadedCount = 3;
    mPixmapList.clear();
}

void PDFViewer::timeOutSlot()
{
    QtConcurrent::run([this](){
        const double resX = physicalDpiX() * 2.0;
        const double resY = physicalDpiY() * 2.0;
        if (mLoadedCount < mDocument->numPages()) {
            Poppler::Page *page = mDocument->page(mLoadedCount);
            if (page) {
                mLoadedCount += 1;
                QImage image = page->renderToImage(resX, resY);
                if (!image.isNull()) {
                    QPixmap pix = QPixmap::fromImage(image);
                    mPixmapList.append(pix);
                    emit pixChanged();
                }
            }
        }
    });
}

void PDFViewer::pixChangedSlot()
{
    if (mPixmapList.size() > 0) {
        for (QPixmap &pix: mPixmapList) {
            QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pix);
            mScene->addItem(item);
            item->setPos(0, mYOffset);
            mYOffset += pix.height();
            if (pix.width() > mSceneWidth) {
                mSceneWidth = pix.width();
            }
        }
        mScene->setSceneRect(0, 0, mSceneWidth, mYOffset);
        mPixmapList.clear();
        mTimer->start();
    }
}

void PDFViewer::wheelEvent(QWheelEvent *event)
{
    if (event->modifiers() & Qt::ControlModifier) {
        // 按住Ctrl键时缩放
        double scaleFactor = 1.15;
        if (event->delta() > 0) {
            scale(scaleFactor, scaleFactor);
        } else {
            scale(1 / scaleFactor, 1 / scaleFactor);
        }
    } else {
        QGraphicsView::wheelEvent(event);
    }
}

效果如下:

7. 编译好的库

poppler-qt5.zip

0

评论区