Qt Windows和Android使用MuPDF预览PDF文件

文章目录

  • [1. Windows MuPDF编译](#1. Windows MuPDF编译)
  • [2. Android MuPDF编译](#2. Android MuPDF编译)
  • [3. 引用 MuPDF 库](#3. 引用 MuPDF 库)
  • [4. 解析本地PDF文件](#4. 解析本地PDF文件)

1. Windows MuPDF编译

使用如下命令将MuPDF的源码克隆到本地

git clone --recursive git://git.ghostscript.com/mupdf.git

直接用VS,打开 mupdf/platform/win32/mupdf.sln 工程文件,然后编译即可,我这边用的是VS2019 编译的x64的版本,编译中并没有报错。 编译完成后会生成 libmupdf.lib 库文件。


2. Android MuPDF编译

使用如下命令将MuPDF的源码克隆到本地

git clone --recursive git://git.ghostscript.com/mupdf-android-viewer.git

(1) 修改 mupdf-android-viewer/jni/libmupdf/platform/java 路径下的 Android.mk 文件,添加

LOCAL_SHORT_COMMANDS := true

...
LOCAL_LDFLAGS := -Wl,--gc-sections
LOCAL_LDFLAGS += $(MUPDF_EXTRA_LDFLAGS)

LOCAL_SHORT_COMMANDS := true
include $(BUILD_SHARED_LIBRARY)

(2) 修改 mupdf-android-viewer/jni/libmupdf/platform/java 路径下的 Application.mk 文件,添加

APP_SHORT_COMMANDS := true

APP_SHORT_COMMANDS := true

ifdef USE_TESSERACT
APP_STL := c++_static
endif

然后打开 AndroidStudio 直接构建即可,最后会生成 libmupdf_java.so 文件,如果找不到可以用everything找一下,我的生成目录是在 mupdf-android-viewer/app/build/intermediates/merged_native_libs/debug/out/lib 下


3. 引用 MuPDF 库

Qt的.pro文件中增加如下配置,分别添加Windows和Android库的头文件和库文件目录

cpp 复制代码
win32 {
    # PDF
    INCLUDEPATH += $$PWD/../thirdLibs/MuPDF/win/include
    CONFIG(debug, debug|release) {
        LIBS += -L$$PWD/../thirdLibs/MuPDF/win/libs/Debug -llibmupdf
    }

    CONFIG(release, debug|release) {
        LIBS += -L$$PWD/../thirdLibs/MuPDF/win/libs/Release -llibmupdf
    }
}

android {
    INCLUDEPATH += $$PWD/../thirdLibs/MuPDF/android/include
    LIBS += -L$$PWD/../thirdLibs/MuPDF/android/libs -lmupdf_java
}

4. 解析本地PDF文件

头文件

cpp 复制代码
#ifndef MUPDFWRAPERCORE_H
#define MUPDFWRAPERCORE_H

#include <QObject>
#include <QImage>

struct fz_context;
struct fz_document;
struct fz_pixmap;

class UTILS_EXPORT MuPDFWraperCore : public QObject
{
    Q_OBJECT

public:
    MuPDFWraperCore(QObject* parent = nullptr);
    ~MuPDFWraperCore();
	
	// 初始化上下文
    void initContext(void);
    // 加载PDF文件
    bool loadPdfFile(const QString& pdfPath);
    // 读取PDF某一页
    QImage loadPdfPage(int nPage, qreal zoom = 100, qreal rotate = 0);
    // 获取总页数
    int getTotalPage(void);

private:
    int m_nTotalPage = 0;

    fz_context *m_pCtx = nullptr;
    fz_document *m_pDoc = nullptr;
    fz_pixmap *m_pPix = nullptr;
};

#endif

cpp文件

cpp 复制代码
#include "MuPDFWraperCore.h"
#include "mupdf/fitz.h"
#include <QDebug>

MuPDFWraperCore::MuPDFWraperCore(QObject* parent)
    :QObject(parent)
{

}

MuPDFWraperCore::~MuPDFWraperCore()
{
    if (m_pPix)
        fz_drop_pixmap(m_pCtx, m_pPix);

    if (m_pDoc)
        fz_drop_document(m_pCtx, m_pDoc);

    if (m_pCtx)
        fz_drop_context(m_pCtx);

    m_pPix = nullptr;
    m_pDoc = nullptr;
    m_pCtx = nullptr;
}

// 初始化上下文
void MuPDFWraperCore::initContext(void)
{
    if (m_pCtx == nullptr)
        m_pCtx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
    if (!m_pCtx) {
        qInfo() << "Create PDF Context Error!";
        return;
    }

    /* Register the default file types to handle. */
    fz_try(m_pCtx)
        fz_register_document_handlers(m_pCtx);
    fz_catch(m_pCtx)
    {
//        fz_report_error(m_pCtx);
        qInfo() << "cannot register document handlers";
        fz_drop_context(m_pCtx);
        m_pCtx = nullptr;
        return;
    }
}

// 加载PDF文件
bool MuPDFWraperCore::loadPdfFile(const QString& pdfPath)
{
    if (m_pCtx == nullptr) {
        initContext();
    }

    if (m_pCtx == nullptr)
        return false;

    /* Open the document. */
    fz_try(m_pCtx)
        m_pDoc = fz_open_document(m_pCtx, pdfPath.toStdString().c_str());
    fz_catch(m_pCtx)
    {
//        fz_report_error(m_pCtx);
        qInfo() << "cannot open document";
        fz_drop_context(m_pCtx);
        m_pCtx = nullptr;
        return false;
    }

    /* Count the number of pages. */
    fz_try(m_pCtx)
        m_nTotalPage = fz_count_pages(m_pCtx, m_pDoc);
    fz_catch(m_pCtx)
    {
//        fz_report_error(m_pCtx);
        qInfo() << "cannot count number of pages";
        fz_drop_document(m_pCtx, m_pDoc);
        fz_drop_context(m_pCtx);
        m_pCtx = nullptr;
        m_pDoc = nullptr;
        return false;
    }

    return true;
}

// 读取PDF某一页
QImage MuPDFWraperCore::loadPdfPage(int nPage, qreal zoom, qreal rotate)
{
    if (m_pCtx == nullptr || m_pDoc == nullptr)
        return QImage();

    if (nPage >= m_nTotalPage) {
        qInfo() << "Page Over Page Total Count";
        return QImage();
    }

    /* Compute a transformation matrix for the zoom and rotation desired. */
    /* The default resolution without scaling is 72 dpi. */
    fz_matrix ctm = fz_scale(zoom / 100, zoom / 100);
    ctm = fz_pre_rotate(ctm, rotate);

    /* Render page to an RGB pixmap. */
    if (m_pPix) {
        fz_drop_pixmap(m_pCtx, m_pPix);
    }
    fz_try(m_pCtx)
        m_pPix = fz_new_pixmap_from_page_number(m_pCtx, m_pDoc, nPage, ctm, fz_device_rgb(m_pCtx), 0);
    fz_catch(m_pCtx)
    {
//        fz_report_error(m_pCtx);
        qInfo() << "cannot render page";
        fz_drop_document(m_pCtx, m_pDoc);
        fz_drop_context(m_pCtx);
        return QImage();
    }

    QImage image(m_pPix->w, m_pPix->h, QImage::Format_RGB888);
    for (int y = 0; y < m_pPix->h; ++y)
    {
        unsigned char* p = &m_pPix->samples[y * m_pPix->stride];
        for (int x = 0; x < m_pPix->w; ++x)
        {
            image.setPixel(x, y, qRgb(p[0], p[1], p[2]));
            p += m_pPix->n;
        }

    }

    return image/*QImage(m_pPix->samples, m_pPix->w, m_pPix->h, QImage::Format_RGB888)*/;
}

// 获取总页数
int MuPDFWraperCore::getTotalPage(void)
{
    return m_nTotalPage;
}

上面的代码比较简单,基本操作API如下:

  • fz_new_context: 创建PDF上下文
  • fz_register_document_handlers: 注册要处理的默认文件类型
  • fz_open_document: 打开PDF文件
  • fz_count_pages: 获取PDF的总页数
  • fz_scale:获取缩放矩阵
  • fz_pre_rotate: 获取旋转矩阵
  • fz_new_pixmap_from_page_number:读取文档某一页并转化为图像

使用如下代码可将 fz_pixmap 转化为 QImage

cpp 复制代码
QImage image(m_pPix->w, m_pPix->h, QImage::Format_RGB888);
for (int y = 0; y < m_pPix->h; ++y)
{
    unsigned char* p = &m_pPix->samples[y * m_pPix->stride];
    for (int x = 0; x < m_pPix->w; ++x)
    {
        image.setPixel(x, y, qRgb(p[0], p[1], p[2]));
        p += m_pPix->n;
    }
}

最后直接渲染这个QImage就完成了PDF的预览 ^v^


效果截图:

Windows-PDF预览:

Android-PDF预览:

相关推荐
m0_748233647 分钟前
RabbitMQ 进阶
android·前端·后端
不想有bug的小菜鸟16 分钟前
vue3使用iframe全屏展示pdf效果
前端·pdf
2501_9032386523 分钟前
Git Bash:Windows下的强大命令行工具
windows·git·bash·个人开发
_GR1 小时前
Qt开发⑧Qt的窗口_下_浮动窗口+对话框
开发语言·css·c++·qt·microsoft
吃汤圆的抹香鲸1 小时前
GoLand 安装包 绿色版 Win,Mac,Linux 包含IntelliJ全家桶 专为Go语言设计的集成开发环境(IDE)
linux·windows·macos·go·intellij-idea·go1.19
吃汤圆的抹香鲸2 小时前
Rider 安装包 绿色版 Win/Mac/Linux 适合.NET和游戏开发者使用 2025全栈开发终极指南:从零配置到企业级实战
linux·运维·windows·sql·游戏·macos·.net
Mr.pyZhang4 小时前
Android构建系统 - 04 编译产物
android·linux
守城小轩10 小时前
Brave 132 编译指南 Android 篇 - 编译准备:系统要求与推荐工具 (三)
android·chrome·chrome devtools·指纹浏览器·浏览器开发·brave
m0_7482384210 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
程序员江同学11 小时前
Kotlin 技术月报 | 2025 年 2 月
android·kotlin