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

相关推荐
BD_Marathon18 小时前
Eclipse 代码自动补全设置
android·java·eclipse
w***744019 小时前
SQL Server 数据库迁移到 MySQL 的完整指南
android·数据库·mysql
g***969021 小时前
【Spring Boot 实现 PDF 导出】
spring boot·后端·pdf
郝学胜-神的一滴1 天前
Qt的QSlider控件详解:从API到样式美化
开发语言·c++·qt·程序人生
郝学胜-神的一滴1 天前
Qt的QComboBox控件详解:从API到样式定制
开发语言·c++·qt·程序人生·个人开发
zgyhc20501 天前
【Android Audio】dumpsys media.metrics分析
android
nono牛1 天前
Android Binder 详解与实践指南
android·binder
小镇学者1 天前
【PHP】PHP WebShell(网页木马)分析
android·开发语言·php
IT小哥哥呀1 天前
如何从 Windows SSH 进入 VirtualBox Ubuntu 虚拟机——密码认证(逐步指南)
linux·windows·ubuntu·ssh·教程·虚拟机
XI锐真的烦1 天前
Flutter Windows 下“Running Gradle task ‘assembleDebug‘...” 卡住一整天的终极解决办法
windows·flutter