Compiling MuPDF DLL to Render and Edit PDF Documents
Note about the downloads:
MupdfSharp contains a pre-compiled MuPDF DLL (versioned 1.12) with a simple C# demo project which utilizes functionalities in the DLL to convert PDF documents into image files.
files_for_compilation contains an extra project file and scripts for you to compile the MuPDF source code to the DLL.
Alternative to downloading the above files, you can also clone my fork on GitHub to have all code files you need and get started with the MuPDF Sharp.sln file in the platform/win32 folder.
MuPDF is an open-sourced, high performance PDF rendering and editing engine written in C. However, the compilation results of its source code do not contain a DLL for use from other languages, such as C#, Visual BASIC, etc.. This article will show you the way to compile the source code to a dynamic link library.
What we need
- Visual Studio 2017 with the C++ desktop development payload and the latest update.
- Windows SDK from 7.0 up. The Windows 10 SDK might also work as well, but I have not tested it.
- (Optional) Python to extract function names to a definition file for the compilation of the DLL file.
- About 2 GB free bytes on your hard drive for code compilation.
- Optionally, use the second download link on top of this article for your convenience of creating the DLL file.
Compiling the source code
The source code of MuPDF is separated to some several directories containing thousands of code files.
- source: the C code files.
- include: the header files.
- thirdparty: the code files of related open-source projects.
- platform: projects for code compilation.
- resources: resources (fonts, character maps, color profiles, PDF names, etc.) used by the engine.
- generated: some code files which convert data from the resources to C code files.
The related project files
Navigate to the platform/win32 directory, you will see quite a few C project files and a solution file named mupdf.sln.
I suggest you make a copy of mupdf.sln and work on that solution file later. NEVER modify the source code unless you know what you are doing and want to participate in contributing to the MuPDF project. Have you modified the source code, you may have trouble merging the source code later, when you want to keep synchonrized with the MuPDF project.
Open that solution file copy with Visual Studio 2017, you will be prompted to upgrade the solution and the projects. Do it. Afterward, you will see more than 10 projects loaded.
The most important projects about compiling a DLL file are listed below.
libmupdf: the project to compile a static library file, libmupdf.lib, which will be used to compile the DLL file.
libresources: the project to handle resources, used by
libthirdparty: the related open-source projects, used by
generated: the automation project which generate some code files for the other projects.
If your goal is to compile the MuPDF DLL file only, the rest projects in the solution are ignorable. So you can safely unload them or remove them from the solution.
The projects must be compiled at the reverse order as they are listed above, from generated to libmupdf, for the code dependencies among them.
Compiling project ‘generated’
When we compile the whole solution, the compiler will firstly begin with the project named ‘generated‘.
During the compilation, you may encounter some problems about missing files and paths.
For instance, the compiler may state that ‘
kernel32.lib‘ is not found. The file ‘
kernel32.lib‘ is one of the static libraries included in the Windows SDK. After the initial project upgrading, the Windows SDK would be set to Windows 8. It may not be presented on your computer, but some other versions of Windows SDK might be available already. It may be in a directory such as ‘C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Lib‘, or somewhere else, such as ‘C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib‘.
Once you find the location of the lib files, add their paths to the project settings so the linker will use them. If you do not have any version of Windows SDK. You might have to download one from Microsoft or install one with the VS Installer which comes with Visual Studio 2017.
As you continue, the compiler may also report that ‘
windows.h‘ is not found. The header files are also part of the Windows SDK. You may find them in the directory such as ‘C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Include‘, and of course, you will need to add the path to the project settings as well.
The version of SDK used to compile this project, generated, will not affect the compatibility of the DLL file that we will create later. The project is used to generate code files and resource codes only.
The following screenshot was taken from my computer. The bold highlighted values indicated that they were modified to include the Windows SDK paths.
Afterward, the project generated should be compiled with no problem.
Compiling project ‘libresources’, ‘libthirdparty’ and ‘libmupdf’
For these projects, change their SDK version properties to 7.0. If you are targeting Windows 8 up and have the Windows 8.1 SDK in Visual Studio installed on your machine, you can skip this procedure. I did this because I never targeted my projects to Windows 8.
Repeat the above procedure and compile those projects one by one. Finally, you will get a static library file named libmupdf.lib, in the output folder.
The SDK version you choose here may affect the compatibility of the DLL. If you have used the Windows 10 SDK here, your DLL may not run on earlier versions of Windows systems.
Creating the DLL
We are very closed to what we want now. To obtain a dynamic library file out of the statlic library file, we need to create another project.
The reason why I don’t modify the libmupdf project to make it generate a DLL file is that I want to keep the other projects that reference libmupdf in the solution be still compilable, and easier the process when we synchorinze the source code files later when the MuPDF source code gets updated.
To create the DLL file, we will have to do three things:
- Create a new project to output a DLL file.
- Reference related static library files to that DLL file.
- Define functions being exposed in the DLL file.
Setting up the DLL project
To create a DLL file, we create a new project in the MuPDF solution. I simply named it DLL in my solution.
Modify the project as the following list by right clicking the project file in the solution explorer and selecting the “Properties” command:
- Change the SDK version to 7.0 (or leave it along, if you are targeting Windows 8 up and installed the Windows 8.1 SDK in Visual Studio installation)
- Change the Configuration to Dynamic Library (.dll).
- Remove the CLR support (optional).
- Switch to the Linker section in the project properties dialog, and set the module definition file to “libmupdf.def” (we will generate this file later).
- Click OK to close the project properties dialog.
Referencing related static library files
Right click the References in the DLL project in the solution explorer. From the popped-up context menu, select the Add Reference command, which will open a dialog. In that dialog, check the checkbox besides libmupdf, libresources and libthirdparty.
The DLL project files are downloadable with this article and you can extract them into the platform\win32 folder and add the project to the MuPDF solution.
Creating the definition file of DLL functions
The above setting will not yet compile a DLL file out of the MuPDF source code, unless we create the missing libmupdf.def function definition list referenced by the project. A definition file is used to expose functions out of the DLL file.
Functions in MuPDF are placed in header files. However, since the project contains quite some scores of header files, it is not a funny task to extract those function names manually.
Fortunately, the developers of SumatraPDF created a Python script to generate that def file for us. You can copy the script file from the repository of SumatraPDF and place it in the scripts folder where you store the source code of MuPDF.
The script need some modifications to reflect the latest changes of MuPDF. I uploaded my own version in this article. If you have Python installed on your computer, double click the script and it will generate the def file.
Having the def file generated, we can start compiling the DLL file.
You may encounter a
LNK2001error at compilation indicating that a certain functions were not resolved. Simply delete those functions from the def file and proceed.
If you don’t have Python and you don’t want to install it, you may have to manually edit the def file with a code editor, or program your own application to generate the def file.
I suggest you use the Python script to do the dirty job for you.
Eventually, you will obtain the DLL file in the platform/win32/Release (or platform/win32/Debug) folder. Where you can find the DLL file depends on your compilation configuration.
Examining exported functions
So far, we have already obtained the DLL file of the MuPDF engine. You can examine whether the functions have been exported by the DLL file with the DLL Export Viewer, a small utility developed by nirsoft.net.
The exported functions in DLL Export Viewer may look like the following picture.
Shrinking the DLL file by excluding unwanted fonts
Some of you may notice that the compiled DLL is a little bit large, up to about 34 megabytes. The reason why it takes so much space is that it has embedded many fonts for international character support. We can shrink its size to about 5 megabytes by excluding those font files from the compiled DLL.
To make the customized DLL, open the libmupdf project, expand the !include/fitz folder and click the config.h file. You can see a lot of comments and definition directives there. Scroll down to the comment with a text “Choose which fonts to include“, and below there are some commented lines, like the following.
/* To avoid all noto fonts except CJK, enable: */ /* #define TOFU */
By defining that
TOFO, you will exclude several huge noto fonts from the DLL file, and reduce the output size from 34 megabytes to about 10 megabytes.
I advice you that DO NOT modify the source code; DO modify the libmupdf project properties and add those definitions to the following place as the screenshot shows.
Recompile the DLL project and check out the size of the DLL file. It should be much smaller.
Comsuming functions in the DLL file
I am not going to write too much in this article on how to comsume the functions in the DLL. For C# programmers, you may use the Platform Invoking technology. Previously I have written an article P/Invoking MuPDF library to render PDF documents. You may reference it by clicking the link above.
The first download link on top of this article has a sample project, which demonstrates how to use P/Invoke from C# to consume functions in the compiled DLL file.
Keeping synchronized with the MuPDF project
As the MuPDF project revolves, the source code may get changed.
Here’s a short summary on keeping synchronized with the MuPDF project.
- If your source code is cloned from GitHub, update it; otherwise you may download the source code from the offical website and overwrite the existing code files. I prefer the former approach since it downloads less bytes at update.
- Open the platform/win32 folder, compare the file modification dates of each project file with the upgraded ones.
- Open the Visual Studio.
- If a certain related projects are changed, DO remove those projects from the Solution Explorer in Visual Studio; DO NOT remove the vcxproj files from the Windows Explorer. If no projects have been changed, skip to step 8 to recompile the DLL file from the source code.
- Add the updated vcproj files back to the solution via Solution Explorer and let Visual Studio updates them and generates the corresponding vcxproj files.
- Repeat the steps in Compiling the source code section to change the Windows SDK and code references.
- Regenerate the def file for DLL exporting if necessary.
- Tweak the libmupdf project to shrink the output DLL file by defining font related preprocessor definitions if you want.
- Recompile the DLL project and get the DLL file.
- Check the commit history of MuPDF project and see whether those header files (*.h files) have changed. If some header files have been changed, use a file merge (comparison) tool to find out what have been changed.
- Change your source code to adapt to the new MuPDF project API if it changes.
- Initial publish on Nov, 2017.