PDF files are one of the most common ways of sharing documents online. Whether we need to pass our clients’ documents to third-party service providers like banks or insurance companies, or just to send a CV to an employer, using a PDF document is frequently the first option.
PDF files can transfer plain/formatted text, images, hyperlinks, and even fillable forms. In this tutorial, we’re going to see how we can fill out PDF forms using PHP and a great PDF manipulation tool called PDFtk Server.
To keep things simple enough, we’ll refer to PDFtk Server
as PDFtk
throughout the rest of the article.
Installation
We’ll use Homestead Improved for our development environment, as usual.
Once the VM is booted up, and we’ve managed to ssh into the system with vagrant ssh
, we can start installing PDFtk
using apt-get
:
sudo apt-get install pdftk
To check if it works, we can run the following command:
pdftk --version
The output should be similar to:
Copyright (c) 2003-13 Steward and Lee, LLC - Please Visit:www.pdftk.com. This is free software; see the source code for copying conditions. There is NO warranty, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
How It Works
PDFtk provides a wide variety of features for manipulating PDF documents, from merging and splitting pages to filling out PDF forms, or even applying watermarks. This article focuses on using PDFtk to fill out a standard PDF form using PHP.
PDFtk uses FDF files for manipulating PDF forms, but what is an FDF file?
FDF or Form Data File is a plain-text file, which can store form data in a much simpler structure than PDF files.
Simply put, we need to generate an FDF file from user submitted data, and merge it with the original PDF file using PDFtk’s commands.
What Is inside an FDF File
The structure of an FDF file is composed of three parts: the header, the content and the footer:
Header
%FDF-1.2
1 0 obj<</FDF<< /Fields[
We don’t need to worry about this part, since it’s what we’re going to use for all the FDF files.
Content
Some sample FDF content is as follows:
<< /T (first_name) /V (John)
<< /T (last_name) /V (Smith)
<< /T (occupation) /V (Teacher)>>
<< /T (age) /V (45)>>
<< /T (gender) /V (male)>>
The content section may seem confusing at first, but don’t worry, we’ll get to that shortly.
Footer
] >> >>
endobj
trailer
<</Root 1 0 R>>
%%EOF
This section is also the same for all of our FDF files.
The content section contains the form data entries, each following a standard pattern. Each line represents one field in the form. They begin with the form element’s name prefixed with /T
, which indicates the title. The second part is the element’s value prefixed with /V
indicating the value:
<< /T(FIELD_NAME)/V(FIELD_VALUE) >>
To create an FDF file, we will need to know the field names in the PDF form. If we have access to a Mac or Windows machine, we can open the form in Adobe Acrobat Pro and see the fields’ properties.
Alternatively, we can use PDFtk’s dump_data_fields
command to extract the fields information from the file:
pdftk path/to/the/form.pdf dump_data_fields > field_names.txt
As a result, PDFtk will save the result in the field_names.txt
file. Below is an example of the extracted data:
--
FieldType: Text
FieldName: first_name
FieldFlags: 0
FieldJustification: Left
---
FieldType: Text
FieldName: last_name
FieldFlags: 0
FieldJustification: Left
---
FieldType: Text
FieldName: occupation
FieldFlags: 0
FieldJustification: Center
---
FieldType: Button
FieldName: gender
FieldFlags: 0
FieldJustification: Center
There are several properties for each field in the form. We can modify these properties in Adobe Acrobat Pro. For example, we can change text alignments, font sizes or even the text color.
PDFtk and PHP
We can use PHP’s exec()
function to bring PDFtk to the PHP environment. Suppose we have a simple PDF form with four text boxes and a group of two radio buttons:
Let’s write a simple script to fill out this form:
<?php
// Form data:
$fname = 'John';
$lname = 'Smith';
$occupation = 'Teacher';
$age = '45';
$gender = 'male';
// FDF header section
$fdf_header = <<<FDF
%FDF-1.2
%,,oe"
1 0 obj
<<
/FDF << /Fields [
FDF;
// FDF footer section
$fdf_footer = <<<FDF
"] >> >>
endobj
trailer
<</Root 1 0 R>>
%%EOF;
FDF;
// FDF content section
$fdf_content = "<</T(first_name)/V({$fname})>>";
$fdf_content .= "<</T(last_name)/V({$lname})>>";
$fdf_content .= "<</T(occupation)/V({$occupation})>>";
$fdf_content .= "<</T(age)/V({$age})>>";
$fdf_content .= "<</T(gender)/V({$gender})>>";
$content = $fdf_header . $fdf_content , $fdf_footer;
// Creating a temporary file for our FDF file.
$FDFfile = tempnam(sys_get_temp_dir(), gethostname());
file_put_contents($FDFfile, $content);
// Merging the FDF file with the raw PDF form
exec("pdftk form.pdf fill_form $FDFfile output.pdf");
// Removing the FDF file as we don't need it anymore
unlink($FDFfile);
Okay, let’s break the script down. First, we define the values that we’re going to write to the form. We can fetch these values from a database table, a JSON API response, or even hardcode them inside the script.
Next, we create an FDF file based on the pattern we discussed earlier. We used the PHP’s tempnam function to create a temporary file for storing the FDF content. The reason is that PDFtk only relies on physical files to perform the operations, especially when filling out forms.
Finally, we called PDFtk’s fill_form
command using PHP’s exec
function. fill_form
merges the FDF file with the raw PDF form. According to the script, our PDF file should be in the same directory as our PHP script.
Save the PHP file above in the web root directory as pdftk.php
. The output will be a new PDF file with all the fields filled out with our data.
It’s as simple as that!
Flattening the Output File
We can also flatten the output file to prevent future modifications. This is possible by passing flatten
as a parameter to the fill_form
command.
<?php
exec("pdftk path/to/form.pdf fill_form $FDFfile output path/to/output.pdf flatten");
Downloading the Output File
Instead of storing the file on the disk, we can force download the output file by sending the file’s content along with the required headers to the output buffer:
<?php
// ...
exec("pdftk path/to/form.pdf fill_form $FDFfile output output.pdf flatten");
// Force Download the output file
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . 'path/to/output.pdf' );
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize('output.pdf'));
readfile('output.pdf');
exit;
If we run the script in the browser, the output file will be downloaded to our machine.
Now that we have a basic understanding of how PDFtk works, we can start building a PHP class around it, to make our service more reusable.
Creating a Wrapper Class around PDFtk
The usage of our final product should be as simple as the following code:
<?php
// Data to be written to the PDF form
$data = [
'first_name' => 'John',
'last_name' => 'Smith',
'occupation' => 'Teacher',
'age' => '45',
'gender' => 'male'
];
$pdf = new pdfForm('form.pdf', $data);
$pdf->flatten()
->save('outputs/form-filled.pdf')
->download();
We’ll create a new file in the web root directory and name it PdfForm.php
. Let’s name the class PdfForm
as well.
Starting with Class Properties
First of all, we need to declare some private properties for the class:
<?php
class PdfForm
{
/*
* Path to raw PDF form
* @var string
*/
private $pdfurl;
/*
* Form data
* @var array
*/
private $data;
/*
* Path to filled PDF form
* @var string
*/
private $output;
/*
* Flag for flattening the file
* @var string
*/
private $flatten;
// ...
}
The Constructor
Let’s write the constructor:
<?php
// ...
public function __construct($pdfurl, $data)
{
$this->pdfurl = $pdfurl;
$this->data = $data;
}
The constructor doesn’t do anything complicated. It assigns the PDF path and the form data to their respective properties.
Handling Temporary Files
Since PDFtk uses physical files to perform its tasks, we usually need to generate temporary files during the process. To keep the code clean and reusable, let’s write a method to create temporary files:
<?php
// ...
private function tmpfile()
{
return tempnam(sys_get_temp_dir(), gethostname());
}
This method creates a file using PHP’s tempnum function. We passed two parameters to the function, the first being the path to the tmp
directory fetched via the sys_get_temp_dir function, and the second parameter being a prefix for the filename, just to make sure the filename would be as unique as possible across different hosts. This will prefix the file name with our hostname. Finally, the method returns the file path to the caller.
Extracting Form Information
As discussed earlier, to create an FDF file, we need to know the name of the form elements in advance. This is possible either by opening the form in Adobe Acrobat Pro or by using PDFtk’s dump_data_fields
command.
To make things easier for the developer, let’s write a method which prints out the fields’ information to the screen. Although we won’t use this method in the PDF generation process, it can be useful when we’re not aware of the field names. Another use case would be to parse the fields’ meta data, to make the writing process more dynamic.
<?php
// ...
public function fields($pretty = false)
{
$tmp = $this->tmpfile();
exec("pdftk {$this->pdfurl} dump_data_fields > {$tmp}");
$con = file_get_contents($tmp);
unlink($tmp);
return $pretty == true ? nl2br($con) : $con;
}
The above method runs PDFtk’s dump_data_fields
command, writes the output to a file and returns its content.
We also set an optional argument for beautifying the output. As a result we’ll be able to get a human friendly output by passing true
to the method. If we need to parse the output or run a regular expression against it, we should call it without arguments.
Creating the FDF File
In the next step, we will write a method for generating the FDF file:
<?php
// ...
public function makeFdf($data)
{
$fdf = '%FDF-1.2
1 0 obj<</FDF<< /Fields[';
foreach ($data as $key => $value) {
$fdf .= '<</T(' . $key . ')/V(' . $value . ')>>';
}
$fdf .= "] >> >>
endobj
trailer
<</Root 1 0 R>>
%%EOF";
$fdf_file = $this->tmpfile();
file_put_contents($fdf_file, $fdf);
return $fdf_file;
}
The makeFdf()
method iterates over the $data
array items to generate the entries based on the FDF standard pattern. Finally, it puts the content in a temporary file using the file_put_contents
function, and returns the file path to the caller.
Flattening the File
Let’s write a method to set the $flatten
attribute to flatten
. This value is used by the generate()
method:
<?php
// ...
public function flatten()
{
$this->flatten = ' flatten';
return $this;
}
Filling out the Form
Now that we’re able to create an FDF file, we can fill the form using the fill_form
command:
// ...
private function generate()
{
$fdf = $this->makeFdf($this->data);
$this->output = $this->tmpfile();
exec("pdftk {$this->pdfurl} fill_form {$fdf} output {$this->output}{$this->flatten}");
unlink($fdf);
}
generate()
calls the makeFdf()
method to generate the FDF file, then it runs the fill_form
command to merge it with the raw PDF form. Finally, it will save the output to a temporary file which is created with the tempfile()
method.
Saving the File
When the file is generated, we might want to save or download it, or do both at the same time.
First, let’s create the save method:
// ...
public function save($path = null)
{
if (is_null($path)) {
return $this;
}
if (!$this->output) {
$this->generate();
}
$dest = pathinfo($path, PATHINFO_DIRNAME);
if (!file_exists($dest)) {
mkdir($dest, 0775, true);
}
copy($this->output, $path);
unlink($this->output);
$this->output = $path;
return $this;
}
The method first checks if there’s any path given for the destination. If the destination path is null, it just returns without saving the file, otherwise it will proceed to the next part.
Next, it checks if the file has been already generated; if not, it will call the generate()
method to generate it.
After making sure the output file is generated, it checks if the destination path exists on the disk. If the path doesn’t exist, it will create the directories and set the proper permissions.
In the end, it copies the file (from the tmp
directory) to a permanent location, and updates the value of $this->output
to the permanent path.
Force Download the File
To force download the file, we need to send the file’s content along with the required headers to the output buffer.
// ...
public function download()
{
if (!$this->output) {
$this->generate();
}
$filepath = $this->output;
if (file_exists($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=' . uniqid(gethostname()) . '.pdf');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
}
}
In this method, first we need to check if the file has been generated, because we might need to download the file without saving it. After making sure that everything is set, we can send the file’s content to the output buffer using PHP’s readfile()
function.
Our PdfForm class is ready to use now. The full code is on GitHub.
Putting the Class into Action
<?php
require 'PdfForm.php';
$data = [
'first_name' => 'John',
'last_name' => 'Smith',
'occupation' => 'Teacher',
'age' => '45',
'gender' => 'male'
];
$pdf = new PdfForm('form.pdf', $data);
$pdf->flatten()
->save('output.pdf')
->download();
Creating an FDF File
If we just need to create an FDF file without filling out a form, we can use the makeFdf()
method.
<?php
require 'PdfForm.php';
$data = [
'first_name' => 'John',
'last_name' => 'Smith',
'occupation' => 'Teacher',
'age' => '45',
'gender' => 'male'
];
$pdf = new PdfForm('form.pdf', $data);
$fdf = $pdf->makeFdf();
The return value of makeFdf()
is the path to the generated FDF file in the tmp
directory. We can either get the contents of the file or save it to a permanent location.
Extracting PDF Field Information
If we just need to see which fields and field types exist in the form, we can call the fields()
method:
<?php
require 'PdfForm.php';
$fields = new PdfForm('form.pdf')->fields();
echo $fields;
If there’s no need to parse the output, we can pass true
to the fields()
method, to get a human readable output:
<?php
require 'PdfForm.php';
$pdf = new PdfForm('pdf-test.pdf')->fields(true);
echo $pdf;
Wrapping Up
We installed PDFtk
and learned some of its useful commands like dump_data_fields
and fill_form
. Then, we created a basic class around it, to show how we can bring PDFtk’s power to our PHP applications.
Please note that this implementation is basic and we tried to keep things as bare bones as possible. We can go further and put the FDF creation feature in a separate class, which would give us more room when working with FDF files. For example, we could apply chained filters to each form data entry like uppercase, lowercase or even format a date, just to name a few. We could also implement download()
and save()
methods for the FDF class.
Questions? Comments? Leave them below and we’ll do our best to reply in a timely manner!
Frequently Asked Questions (FAQs) about Filling PDF Forms with PDFTK and PHP
How can I install PDFTK on my server?
To install PDFTK on your server, you need to follow the instructions specific to your operating system. For Linux, you can use the command sudo apt-get install pdftk
. For Windows, you can download the installer from the PDFTK website and follow the installation wizard. For macOS, you can use Homebrew and run the command brew install pdftk
.
What are the prerequisites for using PDFTK with PHP?
Before you can use PDFTK with PHP, you need to have PHP installed on your server. You also need to have PDFTK installed and the path to the PDFTK binary should be correctly set in your PHP script.
How can I fill a PDF form using PDFTK and PHP?
To fill a PDF form using PDFTK and PHP, you need to create a FDF file with the form data and then use the PDFTK command fill_form
to fill the form. You can execute this command from PHP using the shell_exec
function.
Why is my PDF form not being filled correctly?
If your PDF form is not being filled correctly, it could be due to several reasons. The field names in your FDF file might not match the field names in your PDF form. The PDFTK binary might not be correctly installed or the path to the binary might not be correctly set in your PHP script.
Can I use PDFTK with PHP to fill multiple PDF forms at once?
Yes, you can use PDFTK with PHP to fill multiple PDF forms at once. You need to create a separate FDF file for each form and then use a loop in your PHP script to execute the PDFTK fill_form
command for each form.
How can I flatten a filled PDF form using PDFTK and PHP?
To flatten a filled PDF form using PDFTK and PHP, you can use the PDFTK command output
with the flatten
option. This will create a new PDF file with the form fields permanently filled and not editable.
Can I use PDFTK with PHP to create a PDF form?
No, PDFTK cannot be used to create a PDF form. It can only be used to fill, flatten, and manipulate existing PDF forms.
How can I handle errors when using PDFTK with PHP?
To handle errors when using PDFTK with PHP, you can use the PHP error_reporting
function to display errors. You can also use the try
and catch
statements to catch exceptions and handle them appropriately.
Can I use PDFTK with PHP to extract data from a PDF form?
Yes, you can use PDFTK with PHP to extract data from a PDF form. You can use the PDFTK command dump_data_fields
to extract the form data into a FDF file.
How can I secure a filled PDF form using PDFTK and PHP?
To secure a filled PDF form using PDFTK and PHP, you can use the PDFTK command output
with the encrypt_128bit
option. This will create a new PDF file with the form fields permanently filled and encrypted with a 128-bit key.
A web developer with a solid background in front-end and back-end development, which is what he's been doing for over ten years. He follows two major principles in his everyday work: beauty and simplicity. He believes everyone should learn something new every day.