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预览:

相关推荐
戏谑21 分钟前
Android 常用布局
android·view
7yewh1 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
waicsdn_haha1 小时前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
亽仒凣凣3 小时前
Windows安装Redis图文教程
数据库·windows·redis
炫彩@之星3 小时前
Windows和Linux安全配置和加固
linux·windows·安全·系统安全配置和加固
小奥超人3 小时前
RAR压缩算法的文件修复功能详解
windows·经验分享·winrar·办公技巧
LostSpeed4 小时前
在福昕(pdf)阅读器中导航到上次阅读页面的方法
pdf
旭久4 小时前
SpringBoot的Thymeleaf做一个可自定义合并td的pdf表格
pdf·html·springboot
拭心12 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
mahuifa12 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai