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

相关推荐
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
rockmelodies2 天前
# Windows Server2008 R2 Standard(SP1镜像U盘)重置本地管理员密码【完整详细步骤】
windows·密码破解
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
慧都小项2 天前
当边缘AI上产线:QtitanDocking 如何让工业 HMI 实现多视图协同
人工智能·qt·ui·边缘计算·qt6.3
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透