【问题标题】:Zend_Pdf manipulating PDF Form FieldsZend_Pdf 操作 PDF 表单域
【发布时间】:2011-04-18 08:44:47
【问题描述】:

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

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

所以我有两个基本选择:

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

我知道选项 1 是可行的。我以前做过类似的事情。问题是表格非常复杂,需要计算很多坐标……此外,这个过程有很多试验和错误。

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

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

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

【问题讨论】:

  • 嘿,我在使用 Zend_PDF 时也遇到了同样的情况,您找到可靠的解决方案了吗?我目前正在使用 x/y 选项,但正如您所规定的,当您有密集的表格时,它远非复杂。很想知道您是否有任何解决方案。
  • 其实我从来没有找到一个好的解决方案。最后客户决定他们宁愿有一个格式化的 Excel 文档,所以我们使用phpexcel 来修改我们用作模板的 xlsx 文件。我仍然对解决方案感兴趣,所以如果你通过这个 id 工作,很想听听。

标签: php zend-framework zend-pdf pdf-form


【解决方案1】:

对不起,这有点晚了,但我认为这可能有用...

如果您有权向服务器添加额外的组件,那么您可以使用 PDF Labs PDF Tooklit (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 中的表单字段 - 您可以在以下位置查看更多信息:@ 987654322@

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

正如您可能知道的那样,我刚刚处理了一个非常相似的问题,并且为了找到一个可行的解决方案而经历了很多头痛。

【讨论】:

  • 我最终没有使用这个解决方案,但它确实提供了我想要的,所以我将它标记为答案。
  • 问题是关于 Zend_Pdf 所以要么你改变标题,要么这不是正确的答案
【解决方案2】:

prodigitalson,我想为您发布此解决方案,以防您仍然对想要找到答案感到好奇。它仅适用于针对 1.5 版 (Acrobat 6.0) 优化的 PDF,但它确实运行良好。它是 Zend Framework 1.12.3 的一个非官方补丁,用于填充 PDF 表单字段。 Site with the discussion and patch

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

首先使用以下内容更新您的 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 available here版权所有 (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 文件并替换下面的所有行'+' 在它们前面。您可以通过位置标签 '@@ -202,6 +202,13 @@' 找到它们的位置,它位于第 200 行附近,然后只需复制并粘贴以将旧代码替换为新):

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

【讨论】:

  • 补丁已经包含在 Zend Framework 的最新版本(1.x 分支)中。
猜你喜欢
  • 2011-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
相关资源
最近更新 更多