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