# Getting Started with PDFium

[TOC]

This guide walks through some examples of using the PDFium library. For an
example of using PDFium see the [Chromium PDF Plugin][chrome-plugin].

## Prerequisites

You will need the PDFium library on your computer. You can see the
[README](/README.md) for instructions on getting and installing PDFium.

*** note
You must compile PDFium without both V8 and XFA support for the examples
here to work. V8 can be disabled by setting `pdf_enable_v8 = false` in the
GN args.

See the [V8 Getting Started][pdfium-v8] guide for how to
initialize PDFium when V8 is compiled into the binary.
***

## PDFium Headers

PDFium's API has been broken up over several headers. You only need to include
the headers for functionality you use in your application. The full set of
headers can be found in the [public/ folder of the repository][pdfium-public].

In all cases you'll need to include `fpdfview.h` as it defines the needed
methods for initialization and destruction of the library.

## Initializing PDFium

The first step to using PDFium is to initialize the library. Having done so, one
must destroy the library with `FPDF_DestroyLibrary()` when finished. When
initializing the library, provide the `FPDF_LIBRARY_CONFIG` parameters to
`FPDF_InitLibraryWithConfig()`.

```c
#include <fpdfview.h>

int main() {
  FPDF_LIBRARY_CONFIG config;
  config.version = 2;
  config.m_pUserFontPaths = NULL;
  config.m_pIsolate = NULL;
  config.m_v8EmbedderSlot = 0;

  FPDF_InitLibraryWithConfig(&config);

  FPDF_DestroyLibrary();
  return 0;
}
```

Currently the `config.version` must be set to `2`. Older versions works for
backwards compatibility, but will be deprecated eventually.

`m_pUserFontPaths` can be used to override the font paths searched by PDFium. To
use a custom font paths, pass a `NULL` terminated list of `const char*` paths to
use.

`m_pIsolate` and `m_v8EmbedderSlot` are both used to configure the V8
JavaScript engine. For the V8 isolate, either provide an isolate through
`m_pIsolate` for PDFium to use to store per-isolate data, or pass `NULL` to tell
PDFium to allocate a new isolate. `m_v8EmbedderSlot` is the embedder data slot
to use in the v8::Isolate to store PDFium data. The value must be in the range
[0, `v8::Internals::kNumIsolateDataSlots`). Typically, 0 is a good choice.

For more information on using Javascript see the [V8 Getting Started][pdfium-v8]
guide.

*** aside
PDFium is built as a set of static libraries. You'll need to specify them all on
the link line in order to compile. My build line was:

```
PDF_LIBS="-lpdfium -lfpdfapi -lfxge -lfpdfdoc -lfxcrt -lfx_agg \
-lfxcodec -lpng -lfx_libopenjpeg -lfx_lcms2 -lfx_freetype -ljpeg \
-lfdrm -lpwl -lbigint -lformfiller -ljavascript -lfxedit"
PDF_DIR=<path/to/pdfium>

clang -I $PDF_DIR/public -o init init.c -L $PDF_DIR/out/Debug -lstdc++ -framework AppKit $PDF_LIBS
```

The `-framework AppKit` as needed as I'm building on a Mac. Internally PDFium
uses C++, which is why `-lstdc++` is required on the link line.
***

## Loading a Document

One of the main objects in PDFium is the `FPDF_DOCUMENT`. The object will allow
access to information from PDFs. There are four ways to to create a
`FPDF_DOCUMENT`. `FPDF_CreateNewDocument` will create an empty object which
can be used to create PDFs. For more information see the
[PDF Editing Guide][pdfium-edit-guide].

Loading an existing document is done in one of three ways: loading from file,
loading from memory, or loading via a custom loader. In all three cases you'll
provide a `FPDF_BYTESTRING` which is the password needed to unlock the PDF, if
encrypted. If the file is not encrypted the password can be `NULL`.

The two simplest methods are loading from file and loading from memory. To load
from file, you'll provide the name of the file to open, including extension. For
loading from memory you'll provide a data buffer containing the PDF and its
length.

```c
FPDF_STRING test_doc = "test_doc.pdf";
FPDF_DOCUMENT doc = FPDF_LoadDocument(test_doc, NULL);
if (!doc) {
  return 1;
}

FPDF_CloseDocument(doc);

```

In all three cases, `FPDF_LoadDocument`, `FPDF_LoadMemDocument`,
`FPDF_LoadCustomDocument` a return of `NULL` indicates an error opening the
document or that the file was not found.

You can use `FPDF_GetLastError` to determine what went wrong.

```c
#include <fpdfview.h>
#include <unistd.h>
#include <stdio.h>

int main() {
  FPDF_LIBRARY_CONFIG config;
  config.version = 2;
  config.m_pUserFontPaths = NULL;
  config.m_pIsolate = NULL;
  config.m_v8EmbedderSlot = 0;

  FPDF_InitLibraryWithConfig(&config);

  FPDF_DOCUMENT doc = FPDF_LoadDocument(test_doc, NULL);
  if (!doc) {
    unsigned long err = FPDF_GetLastError();
    fprintf(stderr, "Load pdf docs unsuccessful: ");
    switch (err) {
      case FPDF_ERR_SUCCESS:
        fprintf(stderr, "Success");
        break;
      case FPDF_ERR_UNKNOWN:
        fprintf(stderr, "Unknown error");
        break;
      case FPDF_ERR_FILE:
        fprintf(stderr, "File not found or could not be opened");
        break;
      case FPDF_ERR_FORMAT:
        fprintf(stderr, "File not in PDF format or corrupted");
        break;
      case FPDF_ERR_PASSWORD:
        fprintf(stderr, "Password required or incorrect password");
        break;
      case FPDF_ERR_SECURITY:
        fprintf(stderr, "Unsupported security scheme");
        break;
      case FPDF_ERR_PAGE:
        fprintf(stderr, "Page not found or content error");
        break;
      default:
        fprintf(stderr, "Unknown error %ld", err);
    }
    fprintf(stderr, ".\n");
    goto EXIT;
  }

  FPDF_CloseDocument(doc);
EXIT:
  FPDF_DestroyLibrary();
  return 0;
```

While the above are simple, the preferable technique is to use a custom loader.
This makes it possible to load pieces of the document only as needed. This is
useful for loading documents over the network.




[chrome-plugin]: https://chromium.googlesource.com/chromium/src/+/main/pdf/
[pdfium-public]: https://pdfium.googlesource.com/pdfium/+/main/public/
[pdfium-v8]: /docs/v8-getting-started.md
[pdfium-edit-guide]: /docs/pdfium-edit-guide.md
