Zend_Pdf 操作 PDF 表单字段

发布于 2024-09-25 09:51:16 字数 519 浏览 3 评论 0原文

我目前有一个项目,有许多表单被处理并存储在数据库中。成功完成后,管理员将收到电子邮件通知,其中包含该表单提交的内容。

问题是对于其中一种表格,我需要它看起来与我拥有的 PDF 格式的邮购版本完全相同。

所以我有两个基本选择:

  1. 找出我需要写入的“字段”的所有坐标,然后在这些坐标上覆盖我绘制的文本
  2. 使用 Acrobat Pro 的表单向导将 pdf 转换为 pdf 表单,然后设置字段值 我知道以编程方式

选项 1 是可行的。我以前也做过类似的事情。问题是表格非常复杂,有很多坐标需要弄清楚......而且,这个过程有很多尝试和错误。

选项 2 似乎会更容易,只要我可以通过迭代或名称/id 访问字段并只需设置值即可。

所以我的问题是,Zend_Pdf 是否支持 PDF 表单字段的操作?除了提交和重置表单操作之外,我在 API 中没有看到任何表明它支持此功能的内容。

此外,如果有其他支持选项 2 的 OO F/OSS PDF 库,我将有兴趣了解它们以及任何替代方法。

I currently have a project there are a number of forms that are processed and stored in the DB. Upon successful completion an admin is notified by email with the contents of that form submission.

The problem is for one of these forms i need it to look exactly like the mail order version which I have in PDF format.

So I have two basic options:

  1. Figure out all the coordinates of the "field" i need to write to, and then overlay my drawn text at those coordinates
  2. Turn the pdf into a pdf form using Acrobat Pro's form wizard and then set the field values programmatically

Option 1 i know is doable. I've done similar things before. The problem is the form is pretty complex and there are A LOT of coordinates to figure out... Moreover, there is a lot of trial and error to this process.

Option 2 seems like it would be easier so long as i can access the fields through iteration or name/id and just set the values.

So my question is, does Zend_Pdf support the manipulation of PDF form fields? I dont see anything in the API other than Submit and Reset form actions that would denote it supports this.

Additionally, if there are other OO F/OSS PDF libraries that would support option 2 i would be interested in hearing about them as well as any alternative approaches.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

酸甜透明夹心 2024-10-02 09:51:16

prodigitalson,我想为您发布这个解决方案,以防您仍然好奇想要找到答案。它仅适用于针对版本 1.5 (Acrobat 6.0) 进行优化的 PDF,但它确实工作得很好。它是 Zend Framework 1.12.3 的一个非官方补丁,用于填写 PDF 表单字段。 包含讨论和补丁的站点

无需安装,无需外部程序,无需坐标

第一次更新你的 php.ini 文件包含如下内容(注意:当我上传这些更改时,我必须在实际的 Web 服务器上更改我的 .ini 文件):

include_path = ".;C:\wamp\www\includes"

请注意:我将所有库内容从“ZendFramework-”移出1.12.3\library' 文件夹放入名为 Zend 的文件夹中:C:\wamp\www\includes\Zend 只是为了方便引用该库(无论如何,这就是您所需要的)。

然后在你的 php 文件中(我使用了“DIRECTORY_SEPARATOR”,这样你就可以在 Win 或 Unix 服务器上使用它,并且我不必根据我的 .php 文件所在的位置进行任何代码更改,我只需要进行服务器配置更改):

require_once('Zend'.DIRECTORY_SEPARATOR.'Loader'.DIRECTORY_SEPARATOR.'Autoloader.php');
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('Zend_');

然后进行实际代码使用:

$pdf = Zend_Pdf::load('input-file-containing-form.pdf');
$pdf->setTextField('name', 'Someone');
$pdf->setTextField('address', '1 Main Street');
$pdf->setTextField('city', 'Cyberspace');
$pdf->save('outputfile.pdf');

或者正如我出于我的目的所做的那样(我还包括了用于通过电子邮件发送完成的就业申请的代码,然后删除 .pdf 文件,以便它不会堵塞我的服务器:attach_mailer_class.php 可在此处获取 版权所有 (c) 2006,Olaf Lederer):

// Write $_POST form data to associative array
foreach ($_POST as $key => $value) { 
    $NameArray[$key] = $value;
}

// Path to PDF application fillable file
$pdf_path = dirname(__FILE__) . "\\docs";
$pdf_filename = 'employment_applicationJBzend.pdf';
$pdf_file_path = $pdf_path . "\\" . $pdf_filename;

// Path to PDF application file save location
$result_path = dirname(__FILE__) . "\\results";
$result_filename = ucfirst($_POST['first_name']) . ucfirst($_POST['last_name']) . $filedatetime . '.pdf';
$result_file_path = $result_path . "\\" . $result_filename;

//Filling PDF fields | Example: $pdf->setTextField('position_applied_for', 'IT Manager');
$pdf = Zend_Pdf::load($pdf_file_path);

foreach ($NameArray as $key1 => $value) {
    foreach($ExceptionArray as $key2 => $value)
    {
        if($key1 == $ExceptionArray[$key2]){
            $boolSetText = false;
            break;
        }else{
            $boolSetText = true;
        }
    }
    if($boolSetText){
        $pdf->setTextField($key1, $NameArray[$key1]); 
    }
}
$pdf->save($result_file_path);

//Create and send message using 'attach_mailer_class.php
$email = new attach_mailer($from_name, $from_mail, $mail_to, $cc = "", $bcc = "", $subject);
$email->text_body = $message;
$email->add_attach_file($result_file_path);
// $email->add_attach_file("ip2nation.zip"); 
$email->process_mail();
unlink($result_file_path);

如果页面不再存在,这里是 PDF.php 的补丁(如果您不知道如何运行实际补丁,基本上您可以浏览 PDF.php 文件并替换下面具有您可以通过位于第 200 行左右的位置标记“@@ -202,6 +202,13 @@”找到它们的位置,然后只需复制并粘贴即可将旧代码替换为新的):

--- Pdf.php.orig    2009-11-15 17:52:57.000000000 +0100
+++ Pdf.php 2010-01-07 04:05:23.000000000 +0100
@@ -202,6 +202,13 @@
      * @var array
      */
     protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
+    
+    /**
+     * List of form fields
+     *
+     * @var array - Associative array, key: name of form field, value: Zend_Pdf_Element
+     */
+    protected $_formFields = array();

     /**
      * Request used memory manager
@@ -315,6 +322,7 @@

             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
             $this->_loadOutlines($this->_trailer->Root);
+            $this->_loadFormfields($this->_trailer->Root);

             if ($this->_trailer->Info !== null) {
                 $this->properties = $this->_trailer->Info->toPhp();
@@ -557,6 +565,61 @@
             $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
         }
     }
+    
+    /**
+     * Load form fields
+     * Populates the _formFields array, for later lookup of fields by name
+     *
+     * @param Zend_Pdf_Element_Reference $root Document catalog entry
+     */
+    protected function _loadFormFields(Zend_Pdf_Element_Reference $root)
+    {
+      if ($root->AcroForm === null || $root->AcroForm->Fields === null) {
+        return;
+      }
+      
+      foreach ($root->AcroForm->Fields->items as $field)
+      {
+          if ( $field->FT->value == 'Tx' && $field->T !== null ) /* We only support fields that are textfields and have a name */
+          {
+              $this->_formFields[$field->T->value] = $field;
+          }
+      }
+      
+      if ( !$root->AcroForm->NeedAppearances || !$root->AcroForm->NeedAppearances->value )
+      {
+        /* Ask the .pdf viewer to generate its own appearance data, so we do not have to */
+        $root->AcroForm->add(new Zend_Pdf_Element_Name('NeedAppearances'), new Zend_Pdf_Element_Boolean(true) );
+        $root->AcroForm->touch();
+      }
+    }
+    
+    /**
+     * Retrieves a list with the names of the AcroForm textfields in the PDF
+     *
+     * @return array of strings
+     */
+    public function getTextFieldNames()
+    {
+      return array_keys($this->_formFields);
+    }
+    
+    /**
+     * Sets the value of an AcroForm text field
+     *
+     * @param string $name Name of textfield
+     * @param string $value Value
+     * @throws Zend_Pdf_Exception if the textfield does not exist in the pdf
+     */
+    public function setTextField($name, $value)
+    {
+      if ( !isset($this->_formFields[$name]))
+        throw new Zend_Pdf_Exception("Field '$name' does not exist or is not a textfield");
+      
+      $field = $this->_formFields[$name];
+      $field->add(new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value) );
+      $field->touch();      
+    }

     /**
      * Orginize pages to tha pages tree structure.

prodigitalson, I wanted to post this solution for you, just in case you were still curious about wanting to find an answer. It only works on PDF's optimized for version 1.5 (Acrobat 6.0), but it does work beautifully. It is an unofficial patch for Zend Framework 1.12.3 to fill PDF Form Fields. Site with the discussion and patch

NO INSTALLATION, NO OUTSIDE PROGRAMS, NO COORDINATES

First update your php.ini file with something like the following (note: i will have to change my .ini file on my actual web server when I upload these changes):

include_path = ".;C:\wamp\www\includes"

Just a note: I moved all of the library contents out from 'ZendFramework-1.12.3\library' folder into a folder called Zend: C:\wamp\www\includes\Zend just for the ease of referencing the library (which is all you need anyways).

Then in your php file (I used 'DIRECTORY_SEPARATOR' so that you can use it on a Win or Unix server and that I won't have to make any code changes depending on where my .php file is, I'll only have to make server configuration changes):

require_once('Zend'.DIRECTORY_SEPARATOR.'Loader'.DIRECTORY_SEPARATOR.'Autoloader.php');
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('Zend_');

And then for the actual code use:

$pdf = Zend_Pdf::load('input-file-containing-form.pdf');
$pdf->setTextField('name', 'Someone');
$pdf->setTextField('address', '1 Main Street');
$pdf->setTextField('city', 'Cyberspace');
$pdf->save('outputfile.pdf');

Or as I did it for my purposes (I also included the code that I used to email my finished employment application and then delete the .pdf file so that it doesn't clog up my server: attach_mailer_class.php available here Copyright (c) 2006, Olaf Lederer):

// Write $_POST form data to associative array
foreach ($_POST as $key => $value) { 
    $NameArray[$key] = $value;
}

// Path to PDF application fillable file
$pdf_path = dirname(__FILE__) . "\\docs";
$pdf_filename = 'employment_applicationJBzend.pdf';
$pdf_file_path = $pdf_path . "\\" . $pdf_filename;

// Path to PDF application file save location
$result_path = dirname(__FILE__) . "\\results";
$result_filename = ucfirst($_POST['first_name']) . ucfirst($_POST['last_name']) . $filedatetime . '.pdf';
$result_file_path = $result_path . "\\" . $result_filename;

//Filling PDF fields | Example: $pdf->setTextField('position_applied_for', 'IT Manager');
$pdf = Zend_Pdf::load($pdf_file_path);

foreach ($NameArray as $key1 => $value) {
    foreach($ExceptionArray as $key2 => $value)
    {
        if($key1 == $ExceptionArray[$key2]){
            $boolSetText = false;
            break;
        }else{
            $boolSetText = true;
        }
    }
    if($boolSetText){
        $pdf->setTextField($key1, $NameArray[$key1]); 
    }
}
$pdf->save($result_file_path);

//Create and send message using 'attach_mailer_class.php
$email = new attach_mailer($from_name, $from_mail, $mail_to, $cc = "", $bcc = "", $subject);
$email->text_body = $message;
$email->add_attach_file($result_file_path);
// $email->add_attach_file("ip2nation.zip"); 
$email->process_mail();
unlink($result_file_path);

If page no longer exists here is the patch for PDF.php (which if you don't know how to run the actual patch, basically you go through your PDF.php file and replace all of the lines that below have a '+' in front of them. You can find where they are by the location tag '@@ -202,6 +202,13 @@' which is right around line 200, then just copy and paste to replace the old code with the new):

--- Pdf.php.orig    2009-11-15 17:52:57.000000000 +0100
+++ Pdf.php 2010-01-07 04:05:23.000000000 +0100
@@ -202,6 +202,13 @@
      * @var array
      */
     protected static $_inheritableAttributes = array('Resources', 'MediaBox', 'CropBox', 'Rotate');
+    
+    /**
+     * List of form fields
+     *
+     * @var array - Associative array, key: name of form field, value: Zend_Pdf_Element
+     */
+    protected $_formFields = array();

     /**
      * Request used memory manager
@@ -315,6 +322,7 @@

             $this->_loadNamedDestinations($this->_trailer->Root, $this->_parser->getPDFVersion());
             $this->_loadOutlines($this->_trailer->Root);
+            $this->_loadFormfields($this->_trailer->Root);

             if ($this->_trailer->Info !== null) {
                 $this->properties = $this->_trailer->Info->toPhp();
@@ -557,6 +565,61 @@
             $this->_originalOpenOutlinesCount = $root->Outlines->Count->value;
         }
     }
+    
+    /**
+     * Load form fields
+     * Populates the _formFields array, for later lookup of fields by name
+     *
+     * @param Zend_Pdf_Element_Reference $root Document catalog entry
+     */
+    protected function _loadFormFields(Zend_Pdf_Element_Reference $root)
+    {
+      if ($root->AcroForm === null || $root->AcroForm->Fields === null) {
+        return;
+      }
+      
+      foreach ($root->AcroForm->Fields->items as $field)
+      {
+          if ( $field->FT->value == 'Tx' && $field->T !== null ) /* We only support fields that are textfields and have a name */
+          {
+              $this->_formFields[$field->T->value] = $field;
+          }
+      }
+      
+      if ( !$root->AcroForm->NeedAppearances || !$root->AcroForm->NeedAppearances->value )
+      {
+        /* Ask the .pdf viewer to generate its own appearance data, so we do not have to */
+        $root->AcroForm->add(new Zend_Pdf_Element_Name('NeedAppearances'), new Zend_Pdf_Element_Boolean(true) );
+        $root->AcroForm->touch();
+      }
+    }
+    
+    /**
+     * Retrieves a list with the names of the AcroForm textfields in the PDF
+     *
+     * @return array of strings
+     */
+    public function getTextFieldNames()
+    {
+      return array_keys($this->_formFields);
+    }
+    
+    /**
+     * Sets the value of an AcroForm text field
+     *
+     * @param string $name Name of textfield
+     * @param string $value Value
+     * @throws Zend_Pdf_Exception if the textfield does not exist in the pdf
+     */
+    public function setTextField($name, $value)
+    {
+      if ( !isset($this->_formFields[$name]))
+        throw new Zend_Pdf_Exception("Field '$name' does not exist or is not a textfield");
+      
+      $field = $this->_formFields[$name];
+      $field->add(new Zend_Pdf_Element_Name('V'), new Zend_Pdf_Element_String($value) );
+      $field->touch();      
+    }

     /**
      * Orginize pages to tha pages tree structure.
愛上了 2024-10-02 09:51:16

抱歉,这有点晚了,但我认为这可能有用...

如果您有权向服务器添加额外的组件,那么您可以使用 PDF Labs PDF Toollit (pdftk) 库 - 它是一个命令行实用程序,但显然可以访问通过 PHP 中的 system/exec/passthru 命令。您可以在此处查看 pdftk 信息: http://www.pdflabs.com/docs/pdftk -man-page/ PDFTK 将允许您合并 PDF、添加背景 PDF 以及在 PDF 中填充表单字段(以及加载更多内容) - 请参阅 fill_form 开关。

如果您可以将 pdftk 添加到您的服务器,那么您还可以使用 Andrew Heiss 的 pdftk-php 类,以便更轻松地从数据库中提取的信息更新 pdf 中的表单字段 - 您可以在以下位置查看更多信息: https://github.com/andrewheiss/pdftk-php/

最后一条评论 - 如果您愿意直接从 HTML 即时创建 PDF,那么迄今为止最好的解决方案是 WKHTML2PDF - http://code.google .com/p/wkhtmltopdf/ - 它基本上就像任何 HTML 屏幕的 PDF 屏幕截图一样工作(比这更复杂一点,但你明白了)。

正如您可能会说的那样,我刚刚正在研究一个非常相似的问题,并且经历了很多令人头痛的事情才能找到可行的解决方案。

Sorry this is a bit late but thought this may be useful...

If you have access to add extra components to your server then you can use PDF Labs PDF Tooklit (pdftk) library - it is a command line utility but can obviously be accessed by system/exec/passthru commands in PHP. You can see pdftk info here: http://www.pdflabs.com/docs/pdftk-man-page/ PDFTK will allow you to merge PDFs, add background PDFs and fill form fields within a PDF (plus loads more) - see the fill_form switch.

If you can add pdftk to your server then you can also use Andrew Heiss's pdftk-php class to make it easier to update the form fields in your pdf from the info pulled from your DB - you can see more info at: https://github.com/andrewheiss/pdftk-php/

One last comment - if you are ever wanting to create PDFs on the fly directly from HTML then by far the best solution is WKHTML2PDF - http://code.google.com/p/wkhtmltopdf/ - it basically works like a PDF screenshot of any HTML screen (a little more complex than that but you get the idea).

As you can probably tell I have just been working on a very similar problem and have gone through soooo many headaches to get a working solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文