当前位置: 首页 > news >正文

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 := trueifdef 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 {# PDFINCLUDEPATH += $$PWD/../thirdLibs/MuPDF/win/includeCONFIG(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/includeLIBS += -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_OBJECTpublic: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预览

相关文章:

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&#xff0c;打开 mupdf/platform/win32/mupdf.sln …...

MongoDB聚合:$replaceWith

r e p l a c e W i t h ‘ 可以将输入文档替换为指定的文档。该操作可以替换输入文档的所有字段&#xff0c;包括 ‘ i d ‘ 字段。使用 ‘ replaceWith可以将输入文档替换为指定的文档。该操作可以替换输入文档的所有字段&#xff0c;包括_id字段。使用 replaceWith‘可以将输…...

Vue 进阶系列丨实现简易VueRouter

‍‍Vue 进阶系列教程将在本号持续发布&#xff0c;一起查漏补缺学个痛快&#xff01;若您有遇到其它相关问题&#xff0c;非常欢迎在评论中留言讨论&#xff0c;达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧&#xff01; 2013年7月28日&#xff0c;尤雨溪第一次在 G…...

unity editor 编辑器 GUID localID LocalFileId 查找问题

//传入对象实例化ID 可以获取到 guid localid guid预设的ID localid 预设内的ID //这个方法有个问题如果在预设编辑器状态下 可能出现查不到 guid localid 原因可能 传入对象是是编辑状态下instanceid 并不是保存状态下的 UnityEditor.AssetDatabase.TryGetGUIDAndLocalF…...

【Mybatis】从0学习Mybatis(2)

前言 本篇文章是从0学习Mybatis的第一篇文章&#xff0c;由于篇幅太长CSDN会限流&#xff0c;因此我打算分开两期来写&#xff0c;这是第二期&#xff01;第一期在这儿&#xff1a;【Mybatis】从0学习Mybatis&#xff08;1&#xff09;-CSDN博客 1.什么是ResultMap结果映射&am…...

ChatGPT高效提问—prompt常见用法(续篇九)

ChatGPT高效提问—prompt常见用法(续篇九) ​ 如何准确地向大型语言模型提出问题,使其更好地理解我们的意图,从而得到期望的答案呢?编写有效的prompt的技巧,精心设计的prompt,获得期望的的答案。 1.1 增加条件 ​ 在各种prompt技巧中,增加条件是最常用的。在prompt中…...

echarts的title标题属性

echarts的title标题属性 title 标题组件&#xff0c;包含主标题和副标题。 位于 option对象第一层. title.text 设置主标题内容title.subtext 设置副标题内容 在 ECharts 2.x 中单个 ECharts 实例最多只能拥有一个标题组件。但是在 ECharts 3 中可以存在任意多个标题组件&am…...

【HTML+CSS】使用CSS中的Position与z-index轻松实现一个简单的自定义标题栏效果

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…...

从零开始:用 Rust 编写你的第一个 Web 服务

Rust 是一种现代、高性能的编程语言&#xff0c;近年来在 Web 开发领域也有了一席之地。本文将介绍如何使用 Rust 编写一个简单的 Web 程序&#xff0c;从搭建开发环境到创建第一个 Web 页面。 1. 开发环境搭建 首先&#xff0c;确保你已经安装了 Rust 工具链。你可以通过在终…...

机器学习复习(8)——逻辑回归

目录 逻辑函数&#xff08;Logistic Function&#xff09; 逻辑回归模型的假设函数 从逻辑回归模型转换到最大似然函数过程 最大似然函数方法 梯度下降 逻辑函数&#xff08;Logistic Function&#xff09; 首先&#xff0c;逻辑函数&#xff0c;也称为Sigmoid函数&#…...

深入解析MySQL 8:事务数据字典的变革

随着数据库技术的不断发展和完善&#xff0c;元数据的管理成为了一个日益重要的议题。在MySQL 8中&#xff0c;一项引人注目的新特性是引入了事务数据字典&#xff08;Transaction Data Dictionary&#xff0c;简称TDD&#xff09;&#xff0c;它改变了元数据的管理方式&#x…...

jquery写表格,通过后端传值,并合并单元格

<!DOCTYPE html> <html> <head><title>Table Using jQuery</title><style>#tableWrapper {width: 100%;height: 200px; /* 设置表格容器的高度 */overflow: auto; /* 添加滚动条 */margin-top: -10px; /* 负的外边距值&#xff0c;根据实际…...

百家cms代审

参考博客&#xff1a; PHP代码审计之旅之百家CMS-腾讯云开发者社区-腾讯云 环境搭建 源码链接如下所示 https://gitee.com/openbaijia/baijiacms 安装至本地后 直接解压到phpstudy的www目录下即可 接下来去创建一个数据库用于存储CMS信息。&#xff08;在Mysql命令行中执行…...

算法学习——LeetCode力扣二叉树篇3

算法学习——LeetCode力扣二叉树篇3 116. 填充每个节点的下一个右侧节点指针 116. 填充每个节点的下一个右侧节点指针 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树…...

强制卸载挂载目录

当遇到磁盘卸载失败提示 device is busy fuser -a 显示所有命令行中指定的文件&#xff0c;默认情况下被访问的文件才会被显示。 -c 和-m一样&#xff0c;用于POSIX兼容。 -k 杀掉访问文件的进程。如果没有指定-signal就会发送SIGKILL信号。结合 –signal -signal 使用指定的信…...

HiveSQL——sum(if()) 条件累加

注&#xff1a;参考文章&#xff1a; HiveSql面试题10--sum(if)统计问题_hive sum if-CSDN博客文章浏览阅读5.8k次&#xff0c;点赞6次&#xff0c;收藏19次。0 需求分析t_order表结构字段名含义oid订单编号uid用户idotime订单时间&#xff08;yyyy-MM-dd&#xff09;oamount订…...

Linux命令行工具使用HTTP代理的方法详解

亲爱的Linux用户们&#xff0c;有没有想过在命令行世界里&#xff0c;你的每一个指令都能悄无声息地穿越千山万水&#xff0c;而不被外界窥探&#xff1f;哈哈&#xff0c;没错&#xff0c;就是通过HTTP代理&#xff01;今天&#xff0c;我们就来一起探索如何在Linux命令行工具…...

idea mavn 中途新建gitignore文件如何生效

两种情况下项目代码中新建gitignore文件如何生效。 第一种情况项目代码下没有模块的情况 直接在该项目代码的根目录下进入git命令行执行&#xff1a; git rm -r --cached . git add . 注意上面两个命令后面都有一个点 第二种情况是有模块的情况 需要进入模块目录执行上…...

Hadoop:认识MapReduce

MapReduce是一个用于处理大数据集的编程模型和算法框架。其优势在于能够处理大量的数据&#xff0c;通过并行化来加速计算过程。它适用于那些可以分解为多个独立子任务的计算密集型作业&#xff0c;如文本处理、数据分析和大规模数据集的聚合等。然而&#xff0c;MapReduce也有…...

9.4 OpenGL帧缓冲:纹理和帧缓冲之间的反馈循环

纹理和帧缓冲之间的反馈循环 Feedback Loops Between Textures and the Framebuffer 当在图形编程中&#xff0c;特别是OpenGL这样的图形API中处理纹理&#xff08;Texture&#xff09;和帧缓冲区&#xff08;Framebuffer&#xff09;时&#xff0c;可能会出现一种称为“反馈循…...

相机图像质量研究(6)常见问题总结:光学结构对成像的影响--对焦距离

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…...

fast.ai 机器学习笔记(二)

机器学习 1&#xff1a;第 5 课 原文&#xff1a;medium.com/hiromi_suenaga/machine-learning-1-lesson-5-df45f0c99618 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这些笔记将继续更…...

vue3 elementplus DateTimePicker 日期时间设置默认时间为当天

DateTimePicker里面有个自带属性 可以实现这个需求&#xff0c;如图&#xff1a; // 设置当前当天时间范围 00: 00: 00 - 23:59:59 const currentDate [setDefaultDate(0), setDefaultDate(1)]const setDefaultDate (type:number ): string > {let t ;let date new Da…...

2024年笔记--centos docker离线安装启动失败

Failed to start Docker Application Container Engine 错误如下&#xff1a; [rootel70 docker]# systemctl start docker.service Job for docker.service failed because start of the service was attempted too often. See "systemctl status docker.service" …...

2024.2.10 DMS(数据库管理系统)初体验

数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件&#xff0c;用于建立、使用和维护数据库&#xff0c;简称DBMS。它对数据库进行统一的管理和控制&#xff0c;以保证数据库的安全性和完整性。用户通过DBMS访问数据库中的数据&#xff0c;数据库管…...

zk集群--集群同步

1.概述 前面一章分析了集群下启动阶段选举过程&#xff0c;一旦完成选举&#xff0c;通过执行QuorumPeer的setPeerState将设置好选举结束后自身的状态。然后&#xff0c;将再次执行QuorumPeer的run的新的一轮循环&#xff0c; QuorumPeer的run的每一轮循环&#xff0c;先判断…...

复习面经哦

1.函数可以变量提升 JavaScript 中的函数存在变量提升的概念&#xff0c;这意味着在执行代码之前&#xff0c;函数声明会被提升到其作用域的顶部。这使得你可以在函数声明之前调用函数。然而&#xff0c;这种行为只适用于函数声明&#xff0c;而不是函数表达式。 下面是一些关…...

c++ STL系列——(二)vector

引言 在现代C编程中&#xff0c;std::vector是最常用的动态数组实现之一&#xff0c;它是C标准模板库&#xff08;STL&#xff09;的一部分。vector提供了一种方式&#xff0c;以单一数据结构来存储元素集合&#xff0c;并且可以动态地调整大小以适应新元素。本文将深入探讨ve…...

STM32能够做到数据采集和发送同时进行吗?

STM32能够做到数据采集和发送同时进行吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「STM32的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&am…...

5.Swift常量

Swift 常量 在 Swift 中&#xff0c;除了可以声明变量&#xff08;使用 var 关键字&#xff09;&#xff0c;还可以声明常量&#xff08;使用 let 关键字&#xff09;。常量在赋值后就不能再修改其值&#xff0c;适合用于存储不会改变的数据。以下是关于 Swift 常量的一些重要…...