PDFToolkit separates the PDF model from external-file processing. The 0.6-series core and three 0.1-series modules use PHP 8.3 or later and CI on PHP 8.3, 8.4 and 8.5. The core retains its Composer name roukmoute/pdf after transfer to the organization; new modules use the pdftoolkit/ prefix.

The core boundary

The core represents the objects and mechanisms described by PDF 1.7 / ISO 32000-1:2008 and the Adobe level 3 supplement: values, references, dictionaries, streams, operators, pages, fonts, images, functions, writing and revisions. Definitions retain their PDF names and references to the Adobe document.

For example, PDF describes /Width, /ColorSpace, /SMask, /FontFile2, /CIDToGIDMap and /ToUnicode entries. It allows font programs and DCT bytes to be embedded; it does not define the entire PNG or OpenType format. Their readers require other specifications and belong to modules. Writing remains in the core because it produces PDF syntax and structure.

Version 0.4 adds native constructions for AcroForm and FDF fields, logical structure, color spaces, patterns and all seven shading types, normalized images, forms, transparency, layers, fonts and CMaps. It also provides LZW and predictors, ISO and Adobe encryption mechanisms, signatures and linearization. Detailed coverage separates these verified behaviors from rules still to complete.

Version 0.5 adds revision generations and free lists, updates after linearization, native navigation, embedded files and collections. Signature field policies apply to written snapshots, and cryptographic results are verified before finalization.

Repository and package Input Produced objects or data
PDFToolkit/pdf — roukmoute/pdf PDF objects assembled by the application PDF file, cross-references and revisions
PDFToolkit/pdf-images — pdftoolkit/pdf-images PNG or JPEG bytes and explicit limits Color/alpha samples or core DCT representation
PDFToolkit/pdf-fonts — pdftoolkit/pdf-fonts TrueType bytes, text and explicit parameters Embeddable program, metrics and mappings for the core
PDFToolkit/pdf-reader — pdftoolkit/pdf-reader PDF bytes and reading budgets Read graph, pages and inherited attributes, references to resolve and encoded or decoded streams

Dependencies go from modules to the core. Modules do not depend on one another. The core knows none of their parsers: an application can directly construct its PDF objects or feed them through another converter. The object model is shared, without parallel copies of primitives or a global registry.

Core 0.6 adds initial PDF 2.0 writing, UTF-8 dictionary text and associated files. This does not expand the optional reader’s PDF 1.0–1.7 scope.

Installing what the application needs

All four packages are available on Packagist: core, reader, images and fonts. Stable constraints are roukmoute/pdf:^0.6.1 for the core and pdftoolkit/pdf-reader:^0.1.7, pdftoolkit/pdf-images:^0.1.7 or pdftoolkit/pdf-fonts:^0.1.7 for modules. Older core tags v0.1.0 and v0.1.1 retain their PHP 8.5 constraint.

To generate PDFs only:

composer require "roukmoute/pdf:^0.6.1"

To add reading of existing PDFs:

composer require "roukmoute/pdf:^0.6.1" "pdftoolkit/pdf-reader:^0.1.7"

To convert images:

composer require "roukmoute/pdf:^0.6.1" "pdftoolkit/pdf-images:^0.1.7"

To analyze TrueType fonts:

composer require "roukmoute/pdf:^0.6.1" "pdftoolkit/pdf-fonts:^0.1.7"

Composer resolves these packages from Packagist and downloads their published distributions. Modules require the stable core transitively; installing the core alone installs no module. Keep the application's composer.lock to pin versions and revisions.

Applications that added GitHub repositories under repositories can remove those entries from composer.json. Composer package names remain identical.

Before 1.0, a new minor release may change names and signatures. The ^0.6.1 and ^0.1.7 constraints allow patches within their series and exclude the next minor release. Consult changelogs before switching series. To work with upcoming development, explicitly request dev-master for the relevant packages.

Module releases 0.1.7 accept core series ^0.2 || ^0.3 || ^0.4 || ^0.5 || ^0.6. An application remaining on 0.2, 0.3 or 0.4 can therefore update them; the commands above explicitly select core 0.6 constructions. The core retains mandatory PHP extensions ctype, iconv and zlib. AES, CMS and signature backends using OpenSSL also require openssl when selected by the application.

Reading an external PDF

The reader receives bytes; the application chooses how to obtain them. DocumentReader, FileBytes and page readers belong to the module's PDF\Reading namespace, while dictionaries and numbers use the same PDF\Syntax\Object classes as the writer.

use PDF\Reading\DocumentReader;
use PDF\Reading\FileBytes;
use PDF\Reading\PageContentLimits;
use PDF\Reading\PageContentsReader;
use PDF\Reading\PageTreeLimits;
use PDF\Reading\PageTreeReader;
use PDF\Reading\ReaderLimits;

$reader = new DocumentReader(new ReaderLimits(maxFileBytes: 8_388_608));
$document = $reader->read(new FileBytes($pdfBytes));
$tree = (new PageTreeReader($document, new PageTreeLimits(
    maxDepth: 64,
    maxNodes: 10_000,
    maxPages: 5_000,
)))->read();
$contentsReader = new PageContentsReader($document, new PageContentLimits(
    maxStreams: 128,
));

printf("Validated page count: %d\n", count($tree));
foreach ($tree as $index => $page) {
    printf(
        "Page %d; rotation: %d; content streams: %d\n",
        $index + 1,
        $page->attributes->rotate->integer(),
        count($contentsReader->read($page)),
    );
}

PageTreeReader::read() checks the entire tree: leaf counts, parents, cycles and repeated identities. It returns a countable, iterable ReadPageTree, with ReadPage objects retaining their document, reference and source dictionary. Their PageAttributes provide effective resources, MediaBox, CropBox and rotation after inheritance from the nearest ancestor. A local resource dictionary replaces its inherited counterpart without merging; Contents remains local.

PageContentsReader supplies EncodedStream objects in document order, without concatenation or decoding. Traversal and stream-count limits are explicit objects; cumulative ReadDocument budgets continue to apply. To obtain decoded bytes, separately request $document->streamData($stream). The page-reading guide explains every step and supplies an executable example with three inherited pages.

PDF values remain objects: get() preserves a reference as a reference, and resolve() requests its loading within this document. Read objects are retained in a document-specific cache; there is no singleton or hidden resolution inside values.

The reader supports xref tables and streams, /Prev revisions, object streams, Flate, ASCIIHex, ASCII85 and RunLength, and some TIFF/PNG predictors. Loading a stream preserves its encoded bytes; streamData() requests decoding. It does not render a page or interpret operators to extract text. PDF 2.0, encryption, hybrid references and unimplemented codecs are explicitly rejected.

Read values cannot be directly passed back to the writer: their references belong to the original file. Graph import and remapping, rewriting, merging and repair remain to be built. Consult the reader documentation and its security checks when choosing budgets. By default, retained binary bytes and total decoded stream bytes are each bounded to 128 MiB, in addition to other limits.

Central documentation, independent installations

This documentation presents the API of all four packages. Module pages carry an optional badge, and their source links use the module commit pinned by the build lock. This adds no dependency to applications installing only the core.

The core development checkout installs modules in require-dev to generate the TrueType and PNG/JPEG tutorials and export all signatures. The corresponding PDF objects remain in the core; only external-format readers and their conversion services are in modules.

The core receives ICC profiles, font programs, already-encoded image data, certificates and media as explicit data. It checks their PDF relationships in the relevant native components; their presence does not implement an ICC engine, codec, page rendering, script execution or signature trust policy. These boundaries are detailed in the component guide and coverage guide.

Guide source at the documented commit ↗