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

文章目录

  • 1. Windows MuPDF编译
  • 2. Android MuPDF编译
  • 3. 引用 MuPDF 库
  • 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库的头文件和库文件目录

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文件

头文件

#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文件

#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

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预览:
Windows-PD预览
Android-PDF预览:
Android-PDF预览

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/377527.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Skywalking开发分布式监控(二)

续上篇&#xff0c;上一篇主要是讲了为啥选skywalking&#xff0c;以及怎么有针对性改造SW Agent&#xff0c;现在我们继续看看如何构建自定义Trace跟踪链 要对SW Agent插件做适当剪裁&#xff0c;原来包括customize插件在内SW 8.9有100多个插件&#xff0c;如果没有作用也就罢…

【网络技术】【Kali Linux】Nmap 嗅探(一)简单扫描

一、实验环境 本次实验进行简单的Nmap扫描&#xff0c;实验使用 Kali Linux 虚拟机和 Ubuntu Linux 虚拟机完成&#xff0c;主机操作系统为 Windows 11&#xff0c;虚拟化平台选择 Oracle VM VirtualBox&#xff0c;如下图所示。 二、实验步骤 1、相关配置 Kali Linux 虚拟机…

windows中的apache改成手动启动的操作步骤

使用cmd解决安装之后开机自启的问题 services.msc 0. 这个命令是打开本地服务找到apache的服务名称 2 .通过服务名称去查看服务的状态 sc query apacheapache3.附加上关掉和启动的命令&#xff08;换成是你的服务名称&#xff09; 关掉命令 sc stop apacheapache启动命令 …

ChatGPT 3.5与4.0:深入解析技术进步与性能提升的关键数据

大家好&#xff0c;欢迎来到我的博客&#xff01;今天我们将详细比较两个引人注目的ChatGPT版本——3.5和4.0&#xff0c;通过一些关键数据来深入解析它们之间的差异以及4.0版本的技术进步。 1. 模型规模与参数 ChatGPT 3.5&#xff1a; 参数数量&#xff1a;约1.7亿个模型层数…

07-Java桥接模式 ( Bridge Pattern )

Java桥接模式 摘要实现范例 桥接模式&#xff08;Bridge Pattern&#xff09;是用于把抽象化与实现化解耦&#xff0c;使得二者可以独立变化 桥接模式涉及到一个作为桥接的接口&#xff0c;使得实体类的功能独立于接口实现类&#xff0c;这两种类型的类可被结构化改变而互不影…

CSS之盒子模型

盒子模型 01-选择器 结构伪类选择器 基本使用 作用&#xff1a;根据元素的结构关系查找元素。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IE…

12 选择排序和堆排序

选择排序 基本思想 每一次从待排序的数据元素中选出最小(或最大)的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完 直接选择排序 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素若它不是这组元素的最后一个(第一个)元素…

relectron框架——打包前端vue3、react为pc端exe可执行程序

文章目录 ⭐前言⭐搭建Electron打包环境&#x1f496; npm镜像调整&#x1f496; 初始化项目&#x1f496; 配置index.js ⭐打包vue3⭐打包react⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于使用electronjs打包前端vue3、react成exe可执行程序。…

【开源】JAVA+Vue+SpringBoot实现房屋出售出租系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

Vulnhub-Empire靶机-详细打靶流程

渗透思路 1.确认靶机IP地址2.端口服务扫描3.敏感目录扫描4.ffuf命令在这个目录下&#xff0c;继续使用ffuf工具扫描 5.ssh私钥爆破1.将私钥写进sh.txt中2.将私钥转换为可以被john爆破的形式3.通过John爆破 6.ssh私钥登陆7.icex64提权8.arsene提权 1.确认靶机IP地址 ┌──(roo…

【WebSocket】微信小程序原生组件使用SocketTask 调用星火认知大模型

直接上代码 微信开发者工具-调试器-终端-新建终端 进行依赖安装 npm install base-64 npm install crypto-js 然后顶部工具栏依次点击 工具-构建npm // index.js const defaultAvatarUrl https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQ…

android studio下开发flutter

文章目录 1. 配置环境 https://flutter.cn/docs/get-started/install2. android studio下开发flutter 1. 配置环境 https://flutter.cn/docs/get-started/install 2. android studio下开发flutter 打开Android Studio -> File -> Settings -> Plugins 搜索Dart插件 …

Golang 基础 Go Modules包管理

Golang 基础 Go Modules包管理 在 Go 项目开发中&#xff0c;依赖包管理是一个非常重要的内容&#xff0c;依赖包处理不好&#xff0c;就会导致编译失败&#xff0c;本文将系统介绍下 Go 的依赖包管理工具。 我会首先介绍下 Go 依赖包管理工具的历史&#xff0c;并详细介绍下…

第4章——深度学习入门(鱼书)

第4章 神经网络的学习 本章的主题是神经网络的学习。这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。本章中&#xff0c;为了使神经网络能进行学习&#xff0c;将导入损失函数这一指标。而学习的目的就是以该损失函数为基准&#xff0c;找出能使它的值达到最…

(力扣)1314.矩阵区域和

给你一个 m x n 的矩阵 mat 和一个整数 k &#xff0c;请你返回一个矩阵 answer &#xff0c;其中每个 answer[i][j] 是所有满足下述条件的元素 mat[r][c] 的和&#xff1a; i - k < r < i k, j - k < c < j k 且(r, c) 在矩阵内。 示例 1&#xff1a; 输入&a…

Visual Studio 2022中创建的C++项目无法使用万能头<bits/stdc++.h>解决方案

目录 发现问题 解决办法 第一步 第二步 第三步 第四步 最后一步 问题解决 发现问题 如果大家也遇到下面这种问题&#xff0c;可能是没有include文件夹中没有bits/stdc.h 解决办法 第一步 打开一个C项目&#xff0c;鼠标移动至头文件上右击&#xff0c;选择转到文档或…

Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo

文章目录 前置知识参考文章环境搭建题目环境调试环境 题目分析附件分析漏洞分析OOBUAF 漏洞利用总结 前置知识 Mojo & Services 简介 chromium mojo 快速入门 Mojo docs Intro to Mojo & Services 译文&#xff1a;利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸原文…

训练集,验证集,测试集比例

三者的区别 训练集&#xff08;train set&#xff09; —— 用于模型拟合的数据样本。验证集&#xff08;validation set&#xff09;—— 是模型训练过程中单独留出的样本集&#xff0c;它可以用于调整模型的超参数和用于对模型的能力进行初步评估。 通常用来在模型迭代训练时…

SpringMVC原理(设计原理+启动原理+工作原理)

文章目录 前言正文一、设计原理1.1 servlet生命周期简述1.2 设计原理小结 二、启动原理2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析2.2 DispatcherServlet 的初始化2.3 DispatcherServlet#initHandlerMappings(...) 初始化示例说明 三、工作原理 前言 …

Open CASCADE学习|创建多段线与圆

使用Open CASCADE Technology (OCCT)库来创建和显示一些2D几何形状。 主要过程如下&#xff1a; 包含头文件&#xff1a;代码首先包含了一些必要的头文件&#xff0c;这些头文件提供了创建和显示几何形状所需的类和函数。 定义变量&#xff1a;在main函数中&#xff0c;定义…