Hello! Welcome to the first lesson in our module on "High-Performance PDF Processing." In the previous module, we assembled a powerful toolkit for C++ concurrency, learning how to use std::async for one-off background tasks and std::condition_variable for more complex, ongoing thread coordination. These skills are essential for keeping your whiteboard application responsive.
Now, we'll apply those skills to a core feature of your project: extracting content from PDF documents. To do this, we need a robust and performant PDF processing library. Our choice is PDFium, the open-source PDF engine that powers Google Chrome. This lesson focuses on the crucial first step: integrating this external library into your project's build process using CMake. Mastering dependency management is a fundamental skill for building any non-trivial C++ application, and this lesson will explore the most common and effective strategies.
Why PDFium and Why is Integration a Topic?
PDFium is an excellent choice for your project. It's written in C++ for high performance, has a stable C-style API for easy integration, and is battle-tested inside Chrome. However, like many large, professional libraries, it has its own complex build system (gn, used by the Chromium project) that doesn't use CMake. Compiling it from source yourself would be a significant and time-consuming detour.
Therefore, the challenge isn't writing the code to use PDFium, but first making it available to your compiler and linker in a clean, reproducible way. This is a classic dependency management problem. We will explore two primary, practical solutions for your project.
Method 1: The Classic Approach - Integrating Pre-built Binaries
The most direct method is to download pre-compiled binaries (the .dll/.so/.dylib and .lib/.a files) and tell CMake where to find them. This approach gives you explicit control over the exact library version you use. A fantastic community project provides regularly updated PDFium binaries.
GitHub - bblanchon/pdfium-binaries: 📰 Binary distribution of PDFium · GitHub
This GitHub repository is our source for pre-built PDFium libraries. We'll use its documentation to guide our setup.
First, read the brief introduction to understand what this repository offers. Next, look at the "Download" section. In the table, identify the correct download for your development environment (e.g., "Windows", "x64" or "macOS", "arm64"). You don't need to download it yet, just know where to find it. Notice there are "PDFium" and "PDFium V8" versions; the V8 version includes a Javascript engine for handling interactive forms, which we don't need. The standard PDFium version is sufficient. Finally, and most importantly, read the section "How to use PDFium in a CMake project". This short guide outlines the three key steps: placing the library files, setting a variable to point to them, and updating CMakeLists.txt.
This guide presents a simple workflow. Let's break down the CMake part to understand what's happening under the hood. The two crucial commands are:
find_package(PDFium): This command searches for a special file namedPDFiumConfig.cmakeorFindPDFium.cmake. The pre-built binary package you download includes this file.target_link_libraries(my_exe pdfium): Once the package is found, it defines CMake targets (in this case, a target namedpdfium) that represent the library. This command links your executable against that library target, automatically handling the necessary include paths and library files.
But how does find_package know where to look? The guide suggests setting an environment variable, PDFium_DIR. CMake automatically checks for _DIR variables as hints. A more common and project-specific method is to use the CMAKE_PREFIX_PATH variable when configuring CMake. This variable is a semicolon-separated list of directories where CMake should look for package configuration files.
For example, if you extracted the PDFium library to C:/dev/pdfium, you could configure your project like this:cmake -S . -B build -D CMAKE_PREFIX_PATH="C:/dev/pdfium"
This approach is fundamental to CMake and works for any library that provides a proper CMake configuration file. The GDAL documentation provides an excellent, more general overview of these concepts. While it's for a different library, the principles are identical.
Building GDAL from source — GDAL documentation
This document provides a deeper look into CMake's dependency-handling mechanisms. We'll use it to understand the general principles behind the PDFium-specific instructions.
Skim through the following sections to get a feel for the standard CMake workflow: Notice the general pattern: configure, build, install. Read about the CMAKE_INSTALL_PREFIX variable, which is a standard way to control where software is installed, and CMAKE_PREFIX_PATH, which we just discussed for finding software. You can find these under the general configure options. Look at the section on package dependent options. This explains the common pattern of using _ROOT variables (like PDFium_DIR) and _INCLUDE_DIR/_LIBRARY variables to manually specify paths to dependencies. Finally, scroll down to the subsection on PDFium. You'll see the same variables (PDFIUM_INCLUDE_DIR, PDFIUM_LIBRARY) that the PDFium package's own CMake script uses internally.
Pros of this method:
- Full, explicit control over the library location and version.
- Conceptually simple: download files and point to them.
Cons:
- Manual process: every developer on your team needs to download the files and configure their environment correctly.
- Less portable: Hardcoded paths or environment variables can be brittle.
Method 2: Modern C++ Dependency Management with vcpkg
While the manual method is effective, the modern C++ ecosystem has embraced package managers to automate this process. One of the most popular is Microsoft's vcpkg. With a package manager, you simply declare your dependencies in a manifest file, and the tool handles downloading, building, and integrating them for you. This is highly reproducible and ideal for collaborative and cross-platform projects.
The following video provides an excellent walkthrough of setting up a CMake project with vcpkg. It uses different libraries (SDL3 and GLUE), but the workflow is exactly what you would use for PDFium.
Cmake and Vcpkg C++ Project Setup
This video by MakingGamesWithBen covers the end-to-end process of setting up a modern, dependency-managed C++ project.
Watch the video to understand the overall philosophy and workflow. Pay close attention to these key steps: Introduction (0:38-1:48): The motivation for using CMake and a package manager. Setup (2:30-3:10): Adding vcpkg to your project as a Git submodule. This is a best practice, as it pins a specific version of the package manager to your project. CMake Integration (5:49-6:17): Pointing CMake to the vcpkg toolchain file. This is the magic that hooks vcpkg into CMake's find_package mechanism. Declaring Dependencies (12:50-15:38): Creating the vcpkg.json manifest file to list the libraries your project needs. This is where you would add pdfium. Linking in CMake (15:38-17:48): Using find_package and target_link_libraries to consume the dependencies that vcpkg provides. Build (17:48-19:18): Running the CMake configure step, which automatically triggers vcpkg to download and build all declared dependencies.
To use this method for your project, you would follow the steps in the video, but your vcpkg.json file would look something like this:
{
"name": "performant-whiteboard",
"version-string": "0.1.0",
"dependencies": [
"pdfium"
]
}
And in your CMakeLists.txt, you would use:
find_package(pdfium CONFIG REQUIRED)
target_link_libraries(your_executable_name PRIVATE pdfium)
Notice the lowercase pdfium. vcpkg normalizes package names.
Pros of this method:
- Automated and Reproducible: Anyone who clones your repo can build it with one command.
- Manages Transitive Dependencies: If PDFium needed other libraries,
vcpkgwould handle them automatically.
Cons:
- Adds a dependency on the
vcpkgtool itself. - Can involve a significant download and build time on the very first configure step.
A Note on FetchContent
Modern CMake also offers another approach called FetchContent. This module can download a dependency's source code (e.g., from a Git repository) and build it as a sub-project within your own build.

While FetchContent is excellent for libraries that are designed to be easily embedded and have simple CMake builds, it is not a good fit for PDFium, whose native build system is not CMake. Attempting to force it would be extremely difficult. For this reason, we will stick to the two more practical methods discussed above.
Recommendation for Your Project
For a project of this scope, which you may want to share, collaborate on, or build on multiple platforms, using the vcpkg package manager is the recommended approach. It represents the current best practice in the C++ community for robust and reproducible dependency management. It automates the tedious parts of the "manual" method while providing a stable and predictable environment for all developers.
Conclusion
You now have a comprehensive understanding of how to integrate an external library like PDFium into a CMake project. We've contrasted the classic, manual approach of linking pre-built binaries with the modern, automated approach of using a package manager like vcpkg.
Key Takeaways:
- Integrating external libraries is a common task in C++ development, and CMake provides several mechanisms to handle it.
- The manual method involves downloading binaries and using
CMAKE_PREFIX_PATHor environment variables to helpfind_packagelocate them. This offers fine-grained control but is less portable. - Package managers like
vcpkgautomate the acquisition, building, and integration of dependencies. You declare what you need in a manifest file (vcpkg.json), and the tool handles the rest. This is the recommended approach for modern, collaborative projects. FetchContentis another powerful CMake feature for building dependencies from source, but it is best suited for libraries with native CMake support.
In our next lesson, "Use std::async to parse a PDF document on a background thread," we will finally put our newly integrated library to work. With PDFium available in our project, we can call its functions to open and process a PDF file asynchronously, ensuring the UI of your whiteboard remains perfectly responsive.