部署
mupdf是一个pdf库,不仅可以显示pdf文件,还可以创建、分割、合并、更改pdf文件。而且,除了pdf以外,它还支持mobi、epub、fb2等其它文件。
所以,如果我们有操作pdf等电子书的开发需求,使用mupdf是一个不错的选择。
需要强调的是,mupdf的核心API是C语言的。
但是,它使用自动化地方式,支持C++、Python、C#等语言绑定。我们在不同的语言中使用的时候,直接使用官方的语言绑定就可以了。
Linux
在Linux系统中,有现成的mupdf的C++绑定开发库。虽然不同的发行版中名字不同,但是差异不大。
比如,在Fedora系统中,C语言的核心库叫做mupdf-devel,C++的绑定库叫做mupdf-cpp-devel。
执行
sudo dnf install mupdf-cpp-devel
之后,就自动安装好了。
安装之后,mupdf的头文件在/usr/include/mupdf目录中,动态链接库文件在/usr/lib64中,叫做libmupdfcpp.so(跟Windows中略有差异)。
Windows
在Windows系统中,需要自己编译一下,编译出C++需要的头文件和库,步骤略多一点。
首先,从github上检出mupdf的源代码。
git clone --recursive git://git.ghostscript.com/mupdf.git
注意,这里必须加上--recursive,因为有些第三方库的源文件需要同时检出。
如果忘了加上这个参数,就需要在检出的目录里,执行子模块的检出命令。
cd mupdf
git submodule update
检出源代码以后,就可以使用Visual Studio进行编译了。方法是打开platform/win32目录里面的mupdf.sln方案文件。
mupdf方案里面,有一个mupdfcpp项目,就是C++语言绑定。
但是,要编译mupdfcpp,需要额外的步骤,因为C++语言绑定的源代码是生成的。在编译mupdfcpp之前,需要先生成它的源文件。
方法是使用scripts/mupdfwrap.py脚本,而且这个脚本需要libclang库。
所以,可以在VS命令提示符里面完成这个过程:
pip install libclang setuptools
cd mupdf
python scripts/mupdfwrap.py -b m01
执行完成以后,会在Python的目录里生成一个mupdfcpp64.dll。把这个dll复制到自己的方案里面,或者放在全局的dll目录里面,就可以使用了。
使用
根据mupdf的语言绑定文档所言,mupdf的C++除了包装了C语言的API以外,还有一些额外的便利。
如,在C语言中使用mupdf的时候,大量的变量都是以fz_开头的,而C++使用了命名空间mupdf。即fz_document,对应mupdf::FzDocument,我们可以直接在源代码中使用
using namespace mupdf;
之后,直接使用驼峰命名的类名。
如,在C语言中使用mupdf的时候,需要先初始化一个fz_context,大量的函数都以这个fz_context为参数。而在C++中,这个上下文不是必须的,mupdf的C++绑定自动初始化了这个变量。
再比如,mupdf的C语言API,使用setjmp()实现了异常机制,我们在代码中需要使用
fz_try {
}
fz_catch {
}
代码块的方式,捕获这种异常。而mupdf的C++绑定,把这种异常与C++的异常机制结合了。
示例
下面写一个小函数,实现加载一个PDF文件,并且把指定的页面渲染成一张图片。
using namespace std;
using namespace mupdf;
FzPixmap
loadPdfPage (const string &path, int page_number)
{
auto doc = FzDocument (path.c_str ());
auto page = doc.fz_load_page (page_number);
// 取得页面大小
auto rect = page.fz_bound_page ();
auto width = rect.x1 - rect.x0;
auto height = rect.y1 - rect.y0;
cout << "pdf page " << page_number << " size: " << width << "," << height
<< endl;
// 设置缩放比例
auto matrix = FzMatrix::fz_scale (1.0f, 1.0f);
// 设置旋转角度
matrix = fz_pre_rotate (matrix, 0.0f);
auto pixmap = page.fz_new_pixmap_from_page (matrix, fz_device_rgb (), 0);
return pixmap;
}