Skip to content

QPDF

QPDF is an open-source PDF transformation and inspection tool (Apache 2.0). Its --check mode validates the structural integrity of a PDF file: cross-reference tables, page tree, stream lengths, linearization parameters, and encryption dictionaries.

  • Malformed or inconsistent cross-reference tables
  • Invalid page tree structure (missing /Type, broken /Parent links)
  • Stream /Length mismatches
  • Linearization parameter errors
  • Encryption dictionary inconsistencies
  • Object numbering and generation number issues
  • Missing or malformed %%EOF markers
Terminal window
# Docker (recommended — no local install needed)
cd docker && docker compose build
# Or install locally as a fallback:
brew install qpdf # macOS
sudo apt-get install qpdf # Ubuntu/Debian

The QpdfValidationTrait provides two assertion methods:

// Validate a PDF file on disk
$this->assertQpdfValid('/path/to/file.pdf');
// Validate in-memory PDF bytes (writes to temp file, validates, cleans up)
$this->assertQpdfValidBytes($pdfBytes);

Both methods:

  1. Try Docker first via DockerToolRunner (image: phpdftk/qpdf)
  2. Fall back to local binary via ExternalToolLocator::find('qpdf')
  3. If neither is available, silently return (the test passes on its existing assertions)
  4. Run qpdf --check <file> and assert exit code 0
  5. On failure, include the full QPDF error output in the assertion message

tests/Support/QpdfValidationTrait.php (Phpdftk\Tests\Support\QpdfValidationTrait)

QPDF validation is applied to every integration test that generates a PDF across all three PDF packages:

PackageTest filesAssertion calls
pdf/core21~25
pdf/writer6~35
pdf/toolkit11~80
Total39~140
Test fileAssertions
SimpleTextTestassertQpdfValid(OUTPUT_FILE)
MultiPageComplexTestassertQpdfValid(OUTPUT_FILE)
GraphicsTestassertQpdfValid(OUTPUT_FILE)
AnnotationsTestassertQpdfValid(OUTPUT_FILE)
FormFieldsTestassertQpdfValid(OUTPUT_FILE)
BookmarksTestassertQpdfValid(OUTPUT_FILE)
PageLabelsTestassertQpdfValid(OUTPUT_FILE)
DocumentFeaturesTestassertQpdfValid(OUTPUT_FILE) + assertQpdfValid($outPath)
ExtGStateIntegrationTestassertQpdfValid(OUTPUT_FILE)
SignedPdfIntegrationTestassertQpdfValid(OUTPUT_FILE)
SignatureFieldIntegrationTestassertQpdfValid(OUTPUT_FILE)
FormAppearancesIntegrationTestassertQpdfValid(OUTPUT_FILE)
MarkupAnnotationsIntegrationTestassertQpdfValid(OUTPUT_FILE)
AnnotationSubtypesTestassertQpdfValid(OUTPUT_FILE)
MultimediaAndThreeDIntegrationTestassertQpdfValid(OUTPUT_FILE)
OpenTypeFontIntegrationTestassertQpdfValid(OUTPUT_FILE)
Type3FontIntegrationTestassertQpdfValid(OUTPUT_FILE)
GraphicsPipelineIntegrationTestassertQpdfValid(OUTPUT_FILE)
XRefStreamIntegrationTestassertQpdfValid(OUTPUT_FILE)
EmbeddedFontsTestassertQpdfValid($outPath)
EmbeddedType1FontTestassertQpdfValid($outPath)

WriterTest, PdfTest, PdfIntegrationTest, KerningIntegrationTest, XmpMetadataTest, UnicodeFontTest — assertions on generate(), toBytes(), and save() outputs.

AnnotationFlattenerTest, PdfStamperTest, PageTransformerTest, TextRedactorTest, PageSlicerTest, PdfMergerTest, PdfEncryptTest, BookmarkEditorTest, PageLabelerTest, FormFillerTest, MetadataEditorTest — assertions on toBytes() and save() outputs.

The QPDF Docker image is built in the test job and runs on every push and pull request:

.github/workflows/ci.yml
- name: Build validation tool images
run: cd docker && docker compose build

Validate any PDF from the command line:

Terminal window
# Check a single file
qpdf --check docs/sample-pdfs/simple_text.pdf
# Check all sample PDFs
for f in docs/sample-pdfs/*.pdf; do
echo "Checking $f..."
qpdf --check "$f"
done
# JSON output for programmatic inspection
qpdf --json docs/sample-pdfs/simple_text.pdf | jq .