C API Guide
Vanilla.PDF is written in C++17 but exposes only an ANSI C interface, ensuring ABI stability across compilers and easy interop with other languages.
This page covers the core concepts you need to use the API effectively: handles, types, memory management, error handling, and versioning.
Header and linking
A single umbrella header provides the entire public API:
#include <vanillapdf/c_vanillapdf_api.h>
Link with CMake:
find_package(vanillapdf CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE vanillapdf::vanillapdf)
Handle system
All objects are represented as opaque handles – typed pointers to internal C++ structures. You never access fields directly; all interaction goes through API functions.
Common handle types:
Handle |
Represents |
|---|---|
|
A PDF document (create, open, save, sign) |
|
Low-level file access (XRef, trailers, objects) |
|
The document root (page tree, outlines, names) |
|
The page tree node containing all pages |
|
A single page (content stream, media box, annotations) |
|
AcroForm tree and individual fields (read/write values) |
|
Walks the PDF structure and reports xref/trailer/stream issues |
|
Pluggable backend for file I/O (in-memory buffer or file stream) |
|
Per-type allocation counters for leak detection in tests |
|
A PKCS#12 key for signing |
|
Abstract signing interface (for custom/smart card signing) |
|
Certificate store for signature verification |
Typical usage pattern:
DocumentHandle* doc = NULL;
CatalogHandle* catalog = NULL;
PageTreeHandle* pages = NULL;
Document_Create("output.pdf", &doc);
Document_GetCatalog(doc, &catalog);
Catalog_GetPages(catalog, &pages);
/* ... use pages ... */
PageTree_Release(pages);
Catalog_Release(catalog);
Document_Release(doc);
For the complete handle hierarchy organized by layer (syntax, semantics, contents, utilities), see Architecture.
Types
All basic interface types are either opaque handle pointers or standard C value
types. Key type definitions from c_types.h:
Type |
C type |
Usage |
|---|---|---|
|
|
Return value for all API functions |
|
|
|
|
|
PDF integer values, version numbers |
|
|
PDF real number values |
|
|
Sizes and counts (platform-dependent) |
|
|
File byte offsets |
|
|
UTF-8 string data |
|
|
Large integer values |
Since C has no built-in boolean, the library defines boolean_type with two
constants: VANILLAPDF_RV_TRUE and VANILLAPDF_RV_FALSE.
For the full list, see the Types API reference.
Memory management
All interface objects are reference counted. When you receive a handle through an output parameter, you own a reference and must release it when done.
FileHandle* file = NULL;
error_type rc = File_Open("input.pdf", &file);
if (rc != VANILLAPDF_ERROR_SUCCESS) {
return rc;
}
/* ... work with the file ... */
File_Release(file); /* caller must release */
Rules:
Always initialize handles to
NULLbefore use.Release handles in reverse order of acquisition.
Never use a handle after releasing it.
Releasing the same handle twice is undefined behavior.
The following macro provides safe cleanup with a NULL guard:
#define SAFE_RELEASE(function_name, handle) \
do { \
if (handle == NULL) { \
break; \
} \
\
error_type __result__ = (function_name(handle)); \
if (VANILLAPDF_ERROR_SUCCESS != __result__) { \
exit(EXIT_FAILURE); \
} \
\
handle = NULL; \
} while(0)
Usage with goto cleanup:
FileHandle* file = NULL;
DocumentHandle* document = NULL;
error_type rc = File_Open(path, &file);
if (rc != VANILLAPDF_ERROR_SUCCESS) { goto cleanup; }
rc = Document_Open(path, &document);
if (rc != VANILLAPDF_ERROR_SUCCESS) { goto cleanup; }
/* ... work with file and document ... */
cleanup:
SAFE_RELEASE(Document_Release, document);
SAFE_RELEASE(File_Release, file);
Note
The release function should not fail as long as the parameter is valid. The error code exists to keep the interface consistent.
Leak detection
For debugging missing _Release calls, the ObjectDiagnostics API reports
the live allocation count per object type. Snapshot before and after a unit
of work, compare, and any non-zero delta points at a leaked handle. See the
ObjectDiagnostics_* functions in c_object_diagnostics.h.
Text strings
PDF text strings are not bare UTF-8; they use PDFDocEncoding by default and
UTF-16BE when prefixed with a byte-order mark. The c_text_string_encoding.h
helpers (TextStringEncoding_DetectEncoding, TextStringEncoding_ToUtf8,
TextStringEncoding_FromUtf8) handle detection and conversion so callers can
work with plain UTF-8 at the boundary.
Error handling
Every function returns error_type. On success, the value is
VANILLAPDF_ERROR_SUCCESS. Any other value indicates an error whose message
is stored in a thread-local buffer.
Retrieve the last error:
error_type print_last_error() {
error_type error = 0;
char* error_message = NULL;
char* error_code_name = NULL;
size_type error_code_name_length = 0;
size_type error_message_length = 0;
RETURN_ERROR_IF_NOT_SUCCESS(Errors_GetLastError(&error));
// Last error message
RETURN_ERROR_IF_NOT_SUCCESS(Errors_GetLastErrorMessageLength(&error_message_length));
if (error_message_length >= SIZE_MAX) {
unsigned long long length_converted = error_message_length;
printf("Buffer size is too big: %llu bytes\n", length_converted);
return VANILLAPDF_TEST_ERROR_FAILURE;
}
error_message = (char*) calloc(error_message_length, sizeof(char));
if (NULL == error_message) {
unsigned long long length_converted = error_message_length;
printf("Could not allocate memory: %llu bytes\n", length_converted);
return VANILLAPDF_TEST_ERROR_FAILURE;
}
RETURN_ERROR_IF_NOT_SUCCESS(Errors_GetLastErrorMessage(error_message, error_message_length));
// error code name
RETURN_ERROR_IF_NOT_SUCCESS(Errors_GetPrintableErrorTextLength(error, &error_code_name_length));
if (error_code_name_length >= SIZE_MAX) {
unsigned long long length_converted = error_code_name_length;
printf("Buffer size is too big: %llu bytes\n", length_converted);
return VANILLAPDF_TEST_ERROR_FAILURE;
}
error_code_name = (char*) calloc(error_code_name_length, sizeof(char));
if (NULL == error_code_name) {
unsigned long long length_converted = error_code_name_length;
printf("Could not allocate memory: %llu bytes\n", length_converted);
return VANILLAPDF_TEST_ERROR_FAILURE;
}
RETURN_ERROR_IF_NOT_SUCCESS(Errors_GetPrintableErrorText(error, error_code_name, error_code_name_length));
if (error_message_length == 0) {
printf("Error %u (%s)\n", error, error_code_name);
} else {
printf("Error %u (%s): %s\n", error, error_code_name, error_message);
}
free(error_message);
free(error_code_name);
return VANILLAPDF_TEST_ERROR_SUCCESS;
}
Error codes
All error codes are defined as extern const error_type in c_values.h:
Code |
Meaning |
|---|---|
|
Operation completed successfully |
|
NULL or invalid parameter passed to function |
|
Operation not available in this build configuration |
|
Operation cancelled by caller (e.g. via callback) |
|
Error in compressed (zlib/Flate) data |
|
License file is not valid |
|
Licensed feature accessed without valid license |
|
Buffer too small for requested operation |
|
Internal error; check error message for details |
|
Invalid object type cast (e.g. integer as string) |
|
File handle already disposed |
|
File not yet initialized (call |
|
Required dependent object not found in file |
|
Low-level parsing error; document may be damaged |
|
Wrong password or key for encrypted document |
|
Inserting a key that already exists in a dictionary |
|
Requested optional entry is empty or absent |
|
Object’s underlying type differs from expected semantic type |
Thread safety
Vanilla.PDF is thread-safe. Key internal objects are protected by mutexes,
reference counting is atomic, and error context is stored in thread_local
buffers so concurrent threads never interfere with each other.
See Architecture for the full list of protected objects and implementation details.
Versioning
Vanilla.PDF follows Semantic Versioning. The C API is stable within a major version: minor releases add functions without removing or changing existing ones, patch releases contain only fixes.
Query the version at runtime:
error_type process_library_info() {
// Misc
string_type library_author = NULL;
// Version info
integer_type library_version_major = 0;
integer_type library_version_minor = 0;
integer_type library_version_patch = 0;
integer_type library_version_build = 0;
// Build time information
integer_type library_build_day = 0;
integer_type library_build_month = 0;
integer_type library_build_year = 0;
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetVersionMajor(&library_version_major));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetVersionMinor(&library_version_minor));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetVersionPatch(&library_version_patch));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetVersionBuild(&library_version_build));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetAuthor(&library_author));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetBuildDay(&library_build_day));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetBuildMonth(&library_build_month));
RETURN_ERROR_IF_NOT_SUCCESS(LibraryInfo_GetBuildYear(&library_build_year));
print_text("Library vanillapdf %d.%d.%d.%d by %s\n",
library_version_major,
library_version_minor,
library_version_patch,
library_version_build,
library_author
);
print_text("Built on %d.%d.%d\n",
library_build_day,
library_build_month,
library_build_year
);
return VANILLAPDF_TEST_ERROR_SUCCESS;
}
Function |
Returns |
|---|---|
|
Major version (backward-incompatible changes) |
|
Minor version (new features, backward-compatible) |
|
Patch version (bug fixes only) |
|
Build metadata (no functional impact) |
|
Library author name |
|
Day of month when library was compiled |
|
Month when library was compiled |
|
Year when library was compiled |
Debugging
Enable diagnostic logging to get internal trace output:
#include "vanillapdf/c_logging.h"
Logging_Enable();
Logging_SetSeverity(LoggingSeverity_Debug);
Log levels from least to most verbose: Error, Warning, Info,
Debug.
Common operations
Open an existing PDF:
FileHandle* file = NULL;
File_Open("input.pdf", &file);
DocumentHandle* doc = NULL;
Document_Open("input.pdf", &doc);
Create a new PDF with a page:
DocumentHandle* doc = NULL;
CatalogHandle* cat = NULL;
PageTreeHandle* pages = NULL;
PageObjectHandle* page = NULL;
Document_Create("new.pdf", &doc);
Document_GetCatalog(doc, &cat);
Catalog_GetPages(cat, &pages);
PageObject_CreateFromDocument(doc, &page);
PageTree_AppendPage(pages, page);
Document_Save(doc, "new.pdf");
Sign a document:
PKCS12KeyHandle* pkcs12 = NULL;
SigningKeyHandle* key = NULL;
PKCS12Key_CreateFromFile("key.p12", "password", &pkcs12);
PKCS12Key_ToSigningKey(pkcs12, &key);
Document_Sign(doc, file, key, settings);
Verify a signature:
TrustedCertificateStoreHandle* store = NULL;
TrustedCertificateStore_Create(&store);
TrustedCertificateStore_LoadSystemDefaults(store);
SignatureVerificationResultHandle* result = NULL;
DigitalSignatureExtensions_Verify(sig, doc, store, settings, &result);
SignatureVerificationStatusType status;
SignatureVerificationResult_GetStatus(result, &status);
See Signature Verification Guide for the full verification guide and Examples for more code samples.
For the complete API reference, see Documents, Files, Page Contents, and Utilities.