将 MuPDF DLL 编译成渲染和编辑 PDF 文档
MuPDF 是一个开源、高性能的 PDF 渲染和编辑引擎,用 C 语言编写。本文介绍了如何将其源代码编译成 DLL,以便在其他编程语言中使用。
- 下载演示 (4.2MB)
关于下载的说明下载包含一个预编译的 MuPDF DLL(版本为 1.19)和一个简单的 C# 演示项目,该项目利用 DLL 中的功能将 PDF 文档转换为图像文件。
作为下载上述文件的替代方案,您还可以克隆我在 GitHub 上的仓库,获取所有需要的代码文件,并开始使用 MuPDF.sln 解决方案文件。
关于 SumatraPDF 的说明一篇先前撰写的文章讨论了从 SumatraPDF 项目(该项目大量使用了 MuPDF)中提取 DLL 文件的可能性。通过使用 Visual Studio 2019 编译 SumatraPDF 项目,可以非常轻松地获取一个名为
libmupdf.dll
的 MuPDF DLL 文件。
我们需要什么
MuPDF 的源代码可以从其官方网站或GitHub.com 上的镜像下载。以下列表包含编译 MuPDF 源代码所需的所有内容。
- Visual Studio 2019,包含C++桌面开发工作负载。
- Windows 10 SDK.
- (可选,但推荐)Python,用于提取函数名称到定义文件,以便编译 DLL 文件。
- 硬盘上约 1GB 的可用空间用于代码编译。
编译源代码
MuPDF 的源代码被分成几个目录,包含成千上万个代码文件。
- source:C 语言代码文件
- include:头文件
- thirdparty:相关开源项目的代码文件
- platform:代码编译项目
- resources:引擎使用的资源(字体、字符映射、颜色配置文件、PDF 名称等)
- scripts:一些帮助编译的代码文件
相关项目文件
导航到 platform/win32 目录,您会看到不少 C 语言项目文件和一个名为 mupdf.sln 的解决方案文件。
注意我建议您复制一份 mupdf.sln,并在此副本上进行后续操作。
切勿修改源代码,除非您知道自己在做什么,并希望为
MuPDF
项目做出贡献。如果您修改了源代码,以后在想要与MuPDF
项目保持同步时,可能会在合并源代码时遇到问题。
打开该解决方案文件,您会看到加载了 10 多个项目。
编译 DLL 文件最重要的项目如下所示
libmupdf
:编译静态库文件 libmupdf.lib 的项目,该库将用于编译 DLL 文件libresources
:处理资源的库项目,供libmupdf
使用libthirdparty
:相关开源库项目,供libmupdf
使用bin2coff
:自动生成其他项目所需字体资源文件的项目。
如果您的目标仅是编译 MuPDF DLL 文件,那么解决方案中的其余项目都可以忽略。因此,您可以安全地卸载或从解决方案中删除它们。
为了处理它们之间的依赖关系,项目必须按照上面列出的相反顺序进行编译,从 bin2coff 到 libmupdf。
编译源项目
在 MuPDF 1.17 之前,我使用 Windows 7 SDK 编译源代码,并且需要升级项目文件才能成功编译。从 MuPDF 1.17 开始,使用了 Windows 10 SDK,Visual Studio 2019 成为官方支持的编译器。我们可以毫无问题地使用 Visual Studio 2019 编译它们。
关于缺失的代码文件:如果您下载的是源代码,则需要搜索网络并下载所有必需的第三方源代码文件。因此,建议使用 Visual Studio 克隆项目,而不是下载。
创建 DLL
现在我们非常接近目标了。
要从静态库文件获取动态库文件,我们需要创建另一个项目。
提示我不修改 libmupdf 项目使其生成 DLL 文件,是因为我想让解决方案中引用 libmupdf 的其他项目仍然可以编译,并在以后同步源代码文件时(当 MuPDF 源代码更新时)简化过程。
要创建 DLL 文件,我们将需要做三件事
- 创建一个新项目以输出 DLL 文件。
- 在该项目中引用相关的静态库文件。
- 定义将在 DLL 文件中导出的函数。
设置 DLL 项目
要创建 DLL 文件,我们在 MuPDF
解决方案中创建一个新的 C 项目。我在我的解决方案中简单地将其命名为 MuPDFLib
。
通过选择我们上面创建的 MuPDFLib
项目并点击“项目”菜单选择“属性”命令,按以下列表修改项目。
- 选择所有配置和所有平台。
- 在 Platform Toolset 中,使用Visual Studio 2019 (v142)。
- 如果 SDK 版本不是 10,请将其更改为10。
- 将配置更改为动态库 (.dll)。
- 切换到项目属性对话框中的链接器/输入部分,并将模块定义文件属性设置为“libmupdf.def”(我们稍后将生成此文件)。
- 单击确定按钮关闭项目属性对话框。
引用相关的静态库文件
在解决方案资源管理器中右键单击 MuPDFLib
项目的引用。从弹出的上下文菜单中,选择添加引用命令,这将打开一个对话框。在该对话框中,勾选 libmupdf、libresources 和 libthirdparty 前面的复选框,如下图所示。
创建 DLL 函数的定义文件
上述设置还不能编译出一个可用的 DLL 文件,除非我们创建项目引用的缺失的 libmupdf.def 函数定义文件。定义文件是一个列表,用于定义可以从编译的 DLL 文件导出的函数。
MuPDF
项目中的函数位于 include 文件夹的头文件中。然而,由于该项目包含相当多的头文件,手动提取这些函数名称不是一件轻松的任务。
幸运的是,SumatraPDF(一个基于 MuPDF
的 PDF 查看器应用程序)的开发人员创建了一个 Python 脚本来为我们生成这个 def 文件。您可以从 SumatraPDF 的仓库复制脚本文件,并将其放置在您存储 MuPDF
源代码的 scripts 文件夹中。
脚本需要一些修改才能反映 MuPDF 的最新更改。我已将我的版本上传到我的 GitHub 仓库。如果您已经在计算机上安装了 Python 2.7,请将脚本文件 gen_libmupdf.def.py 和 util.py 放入 scripts 文件夹,双击 gen_libmupdf.def.py,它将在 platform/win32 文件夹中生成 def 文件。
生成 def 文件后,我们就可以开始编译 DLL 文件了。
注意编译时可能会遇到一些
LNK2001
错误,表明某些函数未解析。只需从自动生成的 def 文件中删除这些函数即可继续。如果您没有 Python 并且不想安装它,则可能需要使用代码编辑器手动编写 def 文件,或编写自己的应用程序来生成 def 文件。
我建议您使用 Python 脚本来完成这项繁琐的工作。
最终,您将在 platform/win32/Release(或 platform/win32/Debug 等)文件夹中获得 DLL 文件。DLL 文件的位置取决于您的编译配置。
检查导出的函数
至此,我们已经获得了 MuPDF
引擎的 DLL 文件。您可以使用 NirSoft 开发的一个小型实用程序 DLL Export Viewer 来检查函数是否已由 DLL 文件导出。
在 DLL Export Viewer 中导出的函数可能如下所示
通过排除不需要的字体来缩小 DLL 文件
有些人可能会注意到编译后的 DLL 文件有点大,高达约 34MB。它占用这么多空间的原因是它嵌入了大量字体以支持国际字符。我们可以通过从编译后的 DLL 中排除这些字体文件,将其大小缩小到约 8MB。
要制作自定义 DLL,请打开 libmupdf
项目,展开 !include/fitz 文件夹,然后点击 config.h 文件。您会看到大量的注释和定义指令。向下滚动到带有文本“选择要包含的字体
”的注释,在其下方,有一些被注释掉的行,如下所示
/*
Choose which fonts to include.
By default we include the base 14 PDF fonts,
DroidSansFallback from Android for CJK, and
Charis SIL from SIL for epub/html.
Enable the following defines to AVOID including
unwanted fonts.
*/
/* To avoid all noto fonts except CJK, enable: */
/* #define TOFU */
如其所示,通过定义 TOFU;TOFU_CJK_EXT
,您将从 DLL 文件中排除几个巨大的 noto 字体和 CJK 扩展字体,并将输出大小从 34MB 减少到约 8MB。
警告请谨慎遵循源代码中的说明:
启用以下定义以避免包含不必要的字体。
如果您通过取消注释上面那行下面的行来修改源代码,您在以后同步更新的源代码时会遇到源代码冲突。
我建议您不要修改源代码;而是修改 libmupdf
项目属性,并在预处理器定义属性中添加定义,如下图所示。注意“TOFU
”之前的“;
”,它将定义与<不同的选项>
分隔开。
重新编译 MuPDFLib
项目,并检查 DLL 文件的大小。它应该会小很多。
使用 DLL 文件中的函数
我不会在这篇文章中写太多关于如何使用 DLL 中函数的内容。对于 C# 程序员,您可以使用平台调用技术。先前,我写了一篇题为使用 P/Invoke 调用 MuPDF 库渲染 PDF 文档的文章。您可以点击上面的链接进行参考。
本文顶部的下载链接包含一个示例项目,演示了如何从 C# 使用 P/Invoke 来调用编译的 DLL 文件中的函数。
与 MuPDF 项目保持同步
随着 MuPDF
项目的不断发展,源代码可能会发生变化。
以下是与 MuPDF
项目保持同步的简要总结。
- 如果您的源代码是从 GitHub 克隆的,请更新它;否则,您可以从官方网站下载源代码并覆盖现有代码文件。我更喜欢前者,因为它在更新时下载的字节更少。
- 更新源代码时,将 GitHub 源代码合并到您的仓库。
- 由于您在上述过程中修改了
libmupdf
项目以缩小 DLL 的大小,因此会发生源代码冲突。我们必须通过合并源代码来解决冲突。合并时使用源(官方代码)。 - 不要忘记更新子模块,方法是右键单击团队资源管理器的更改窗格中的已更新子模块,然后执行相应的命令,否则您的项目可能无法编译。在解决任何子模块冲突时,请使用源。
- 如有必要,请执行
gen_libmupdf.def.py
脚本,为DLL导出重新生成 def 文件。 - 如果您想缩小输出的 DLL 文件,请通过定义与字体相关的预处理器定义来调整
libmupdf
项目。 - 重新编译
MuPDFLib
项目并获取 DLL 文件。 - 如果遇到
LNK2001
错误,请从 def 文件中删除不存在的函数。 - 检查
MuPDF
项目的提交历史,查看项目中的头文件(*.h 文件)是否已更改。发生更改时,通过比较提交历史来找出已更改的内容。 - 如果 MuPDF 的版本发生变化,则必须更新您的源代码以适应新的
MuPDF
项目API。
历史
- 首次发布于 2017 年 11 月
- 更新了 GitHub 分支中的源代码,并修复了字体资源部分中的拼写错误。
- 更新了 GitHub 分支中的源代码,重新编译了 DLL,更新了文章以反映 2018 年 11 月 24 日的最新源代码更改。
- 更新了 GitHub 分支中的源代码,重新编译了 DLL,更新了文章以添加更多关于与 MuPDF 项目同步的详细信息,并反映 2019 年 7 月 18 日的最新源代码更改。
- 将 GitHub 分支中的源代码更新到 MuPDF 1.16,重新编译了 DLL,在演示项目中修复了 P/Invoke 调用约定问题,于 2019 年 8 月 1 日。
- 修改了文章,并将 GitHub 分支中的源代码更新到 MuPDF 1.17rc,于 2020 年 4 月 28 日重新编译了 DLL。