# pdftk 图像转pdf_用PDFtk和PHP填写PDF表单

pdftk 图像转pdf

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文件是在线共享文档的最常用方法之一。 无论是需要将客户的文件传递给银行或保险公司等第三方服务提供商，还是仅仅将简历发送给雇主，使用PDF文档通常都是首选。

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.

PDF文件可以传输纯文本/格式化的文本，图像，超链接，甚至可以填写表格。 在本教程中，我们将了解如何使用PHP和称为PDFtk Server的出色PDF操作工具来填写PDF表单。

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提供了多种功能来处理PDF文档，从合并和拆分页面到填写PDF表单，甚至应用水印。 本文重点介绍使用PDFtk来使用PHP填写标准PDF表单。

PDFtk uses FDF files for manipulating PDF forms, but what is an FDF file?

PDFtk使用FDF文件处理PDF表单，但是什么是FDF文件？

FDF or Form Data File is a plain-text file, which can store form data in a much simpler structure than PDF files.

FDF或表单数据文件是纯文本文件，可以用比PDF文件简单得多的结构存储表单数据。

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.

### FDF文件中的内容 (What Is inside an FDF File)

The structure of an FDF file is composed of three parts: the header, the content and the footer:

FDF文件的结构由三部分组成：标头，内容和页脚：

%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.

] >> >>
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和PHP (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");

header('Content-Disposition: attachment; filename=' . 'path/to/output.pdf' );
header('Content-Length: ' . filesize('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.

## 在PDFtk周围创建包装器类 (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. 上面的方法运行PDFtk的dump_data_fields命令，将输出写入文件并返回其内容。 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. 我们还设置了一个可选参数来美化输出。 结果，通过将true传递给该方法，我们将能够获得人性化的输出。 如果需要解析输出或对输出运行正则表达式，则应不带参数调用它。 ### 创建FDF文件 (Creating the FDF File) In the next step, we will write a method for generating the FDF file: 在下一步中，我们将编写一种生成FDF文件的方法： <?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.

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: 现在我们已经可以创建FDF文件了，我们可以使用fill_form命令填写表单： // ... 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. generate()调用makeFdf()方法生成FDF文件，然后运行fill_form命令将其与原始PDF表单合并。 最后，它将输出保存到使用tempfile()方法创建的临时文件中。 ### 保存文件 (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. 最后，它将文件(从tmp目录)复制到永久位置，并将$this->output的值更新为永久路径。

To force download the file, we need to send the file’s content along with the required headers to the output buffer.

// ...

{
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. 在这种方法中，首先我们需要检查文件是否已生成，因为我们可能需要下载文件而不保存文件。 确保一切都设置好之后，我们可以使用PHP的readfile()函数将文件的内容发送到输出缓冲区。 Our PdfForm class is ready to use now. The full code is on GitHub. 我们的PdfForm类现在可以使用了。 完整代码在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(); ### 创建一个FDF文件 (Creating an FDF File) If we just need to create an FDF file without filling out a form, we can use the makeFdf() method. 如果只需要创建FDF文件而不填写表单，则可以使用makeFdf()方法。 <?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.

makeFdf()的返回值是tmp目录中生成的FDF文件的路径。 我们可以获取文件的内容或将其保存到永久位置。

### 提取PDF字段信息 (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!

pdftk 图像转pdf

• 0
点赞
• 0
评论
• 2
收藏
• 扫一扫，分享海报

04-24 5302

09-20 1891
10-11 811
03-28 697
07-22 657
10-08
©️2022 CSDN 皮肤主题：编程工作室 设计师：CSDN官方博客

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。