PDFium
| Language | C++ with a C API |
|---|---|
| License | BSD-3-Clause (some third-party parts Apache 2.0) |
| Repository | pdfium.googlesource.com/pdfium |
| Documentation | comments in the public headers |
| Depends on | bundled (FreeType, libjpeg-turbo, OpenJPEG, lcms2, zlib, libpng); optional V8 for JavaScript and XFA forms |
| Read / write | yes / yes: render, extract text, forms, import pages, create pages with text, images and paths |
PDFium is the PDF engine of Chrome, Edge and Android’s PDF viewer, derived from Foxit’s code. It renders accurately, handles broken files well, extracts text with character positions, fills AcroForms (and XFA with V8), and has an editing API to create pages, add text, image and path objects, import pages from other documents and save. The BSD license makes it the usual choice when MuPDF’s AGPL is a problem; pdfium-render (Rust), pypdfium2 (Python) and PDFtoImage (.NET) wrap it.
Building PDFium needs Chromium’s depot_tools and gn. Most projects use the prebuilt shared libraries from bblanchon/pdfium-binaries instead, published for Windows, macOS, Linux, Android, iOS and WebAssembly. The API is C and works with handles (FPDF_DOCUMENT, FPDF_PAGE, FPDF_BITMAP); strings are UTF-16LE.
Installation#
Download a release archive for your platform from pdfium-binaries releases: it contains include/, the shared library (pdfium.dll, libpdfium.so, libpdfium.dylib) and a CMake config:
set(PDFium_DIR "${CMAKE_SOURCE_DIR}/pdfium")
find_package(PDFium REQUIRED)
target_link_libraries(myapp PRIVATE pdfium)
Building from source:
# needs depot_tools in PATH
mkdir pdfium-src && cd pdfium-src
gclient config --unmanaged https://pdfium.googlesource.com/pdfium.git
gclient sync
cd pdfium && gn args out/Release # e.g. is_debug=false pdf_enable_v8=false pdf_enable_xfa=false
ninja -C out/Release pdfium
Basic usage: render a page to a bitmap#
#include <fpdfview.h>
#include <stdio.h>
int main(void) {
FPDF_InitLibrary();
FPDF_DOCUMENT doc = FPDF_LoadDocument("in.pdf", NULL /*password*/);
if (!doc) {
fprintf(stderr, "load error %lu\n", FPDF_GetLastError());
FPDF_DestroyLibrary();
return 1;
}
printf("%d pages\n", FPDF_GetPageCount(doc));
FPDF_PAGE page = FPDF_LoadPage(doc, 0);
double scale = 150.0 / 72; // 150 dpi
int w = (int)(FPDF_GetPageWidthF(page) * scale);
int h = (int)(FPDF_GetPageHeightF(page) * scale);
FPDF_BITMAP bmp = FPDFBitmap_Create(w, h, 0 /*no alpha*/);
FPDFBitmap_FillRect(bmp, 0, 0, w, h, 0xFFFFFFFF); // white background
FPDF_RenderPageBitmap(bmp, page, 0, 0, w, h, 0 /*rotation*/, FPDF_ANNOT);
// BGRA pixels, top row first
unsigned char *pixels = FPDFBitmap_GetBuffer(bmp);
int stride = FPDFBitmap_GetStride(bmp);
printf("%d x %d, stride %d, first pixel B=%u\n", w, h, stride, pixels[0]);
// ... encode pixels with libpng, stb_image_write, etc.
FPDFBitmap_Destroy(bmp);
FPDF_ClosePage(page);
FPDF_CloseDocument(doc);
FPDF_DestroyLibrary();
return 0;
}
Add FPDF_LCD_TEXT or FPDF_PRINTING to the flags for other rendering modes. To render form fields, set up a form handle with FPDFDOC_InitFormFillEnvironment and call FPDF_FFLDraw after rendering the page.
How to extract text from a PDF page#
FPDFText_GetText writes UTF-16LE code units, including a terminating zero:
#include <fpdf_text.h>
#include <stdlib.h>
FPDF_TEXTPAGE text = FPDFText_LoadPage(page);
int count = FPDFText_CountChars(text);
unsigned short *buf = malloc((count + 1) * sizeof(unsigned short));
int written = FPDFText_GetText(text, 0, count, buf);
// buf holds written-1 UTF-16LE code units; convert to UTF-8 for printing
for (int i = 0; i < written - 1; i++) {
if (buf[i] < 128) putchar(buf[i]); // ASCII only, for the example
}
free(buf);
FPDFText_ClosePage(text);
FPDFText_GetCharBox and FPDFText_GetCharOrigin give the position of each character; FPDFText_FindStart searches text.
How to read PDF metadata#
#include <fpdf_doc.h>
char buf[512]; // receives UTF-16LE text with a terminating zero
unsigned long len = FPDF_GetMetaText(doc, "Title", buf, sizeof buf);
// len is the size in bytes; 2 means empty
int version = 0;
FPDF_GetFileVersion(doc, &version); // 17 for PDF 1.7
Keys: Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate.
How to merge PDFs and save#
FPDF_ImportPages copies pages (a range like "1-3,5", or NULL for all) into another document. Saving goes through a callback that receives the bytes:
#include <fpdf_ppo.h>
#include <fpdf_save.h>
#include <fpdf_edit.h>
#include <stdio.h>
typedef struct {
FPDF_FILEWRITE base; // must be first
FILE *fp;
} FileWriter;
static int write_block(FPDF_FILEWRITE *self, const void *data, unsigned long size) {
FileWriter *w = (FileWriter *)self;
return fwrite(data, 1, size, w->fp) == size;
}
int merge(const char *a, const char *b, const char *out) {
FPDF_DOCUMENT dst = FPDF_CreateNewDocument();
FPDF_DOCUMENT src1 = FPDF_LoadDocument(a, NULL);
FPDF_DOCUMENT src2 = FPDF_LoadDocument(b, NULL);
if (!src1 || !src2) return 1;
FPDF_ImportPages(dst, src1, NULL, 0);
FPDF_ImportPages(dst, src2, NULL, FPDF_GetPageCount(dst));
FileWriter w = {{1, write_block}, fopen(out, "wb")};
FPDF_SaveAsCopy(dst, &w.base, FPDF_NO_INCREMENTAL);
fclose(w.fp);
FPDF_CloseDocument(src1);
FPDF_CloseDocument(src2);
FPDF_CloseDocument(dst);
return 0;
}
How to rotate pages#
FPDFPage_SetRotation takes 0-3 (multiples of 90 degrees clockwise). Save the document afterwards with FPDF_SaveAsCopy:
for (int i = 0; i < FPDF_GetPageCount(doc); i++) {
FPDF_PAGE p = FPDF_LoadPage(doc, i);
FPDFPage_SetRotation(p, 1); // 90 degrees
FPDF_ClosePage(p);
}
How to create a PDF with text#
FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792); // US Letter in points
FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(doc, "Helvetica", 24.0f);
// FPDF_WIDESTRING is UTF-16LE, zero-terminated
const unsigned short hello[] = {'H', 'e', 'l', 'l', 'o', 0};
FPDFText_SetText(text, hello);
FPDFPageObj_Transform(text, 1, 0, 0, 1, 72, 700); // move to x=72, y=700
FPDFPage_InsertObject(page, text);
FPDFPage_GenerateContent(page); // write the content stream
// save with FPDF_SaveAsCopy as in the merge example
FPDF_ClosePage(page);
Tips#
- PDFium isn’t thread-safe: use one thread for all calls, or a global lock.
FPDF_InitLibraryWithConfigsets the V8 isolate and font paths when you need JavaScript, XFA or custom fonts.- PDFium can’t encrypt documents. Use QPDF for that.