发新话题
打印

Zend 框架学习之创建 PDF 文件篇

添加图像

首先要为文档添加图片。在撰写本文时,Zend 框架只支持 JPEG 图片,但这种情况有可能会改变。

我们首先来创建一个图片对象,如下所示。


清单 4. 添加图片
复制内容到剪贴板
代码:
<?php
require_once 'Zend/Pdf.php';

$pdf = new Zend_Pdf();
$page = new Zend_Pdf_Page(Zend_Pdf_Const::PAGESIZE_LETTER);
$chompImage = new Zend_Pdf_Image_JPEG(
                      dirname(__FILE__) . '/chomp.jpg');

$pageHeight = $page->getHeight();
$pageWidth = $page->getWidth();
$imageHeight = 72;
$imageWidth = 72;

$topPos = $pageHeight - 36;
$leftPos = 36;
$bottomPos = $topPos - $imageHeight;
$rightPos = $leftPos + $imageWidth;

$page->drawImage($chompImage, $leftPos, $bottomPos, $rightPos, $topPos);

$pdf->pages[0] = ($page);

$pdf->save("chomp.pdf");

?>
请注意:如果在 PHP 中没有可用的图片扩展库,在执行这个文件时就会出现错误。为解决这个问题,请打开您的 php.ini 文件,并将写有 extension=php_gd2.dll 的代码行的注释去掉。

创建真实的图片对象是不解自明的。我们只从一个文件中装载 JPEG。但从那开始,我们就需要思考关于定位的问题,这可就不那么明显了。

PDF 文档使用了与 PostScript 文件一样的准则。这意味着一切都以 “点” 来衡量,而非像素。1 英寸等于 72 点,所以如果我们想要一个长宽均为 1 英寸的图片,则需要将长和宽的值都设为 72。

至于实际的定位,坐标系的原点是页面的左下角。换言之,点 0,0 与左下角相对应,坐标值随页面高度和宽度的增加而增加。

所以,要将对象放到离页面顶部半英寸的地方,则需要将图片顶部的坐标设置为页面高度减去 36 点,或者说半英寸。同理,图片底部的坐标就是顶部坐标减去图片高度。因为要将图片放到左手的角落,所以要先把左边的坐标设置为 36 点,然后加上图片宽度作为右边的坐标值。

了解上述信息之后,就可以使用 drawImage() 方法将图片添加到实际的页面中。添加文本的过程与此类似。

TOP

添加文本

在向页面添加文本之前,需要先决定它的外观。这可以通过使用样式来实现,如下所示。


清单 5. 添加文本
复制内容到剪贴板
代码:
...
$topPos = $pageHeight - 36;
$leftPos = 36;
$bottomPos = $topPos - $imageHeight;
$rightPos = $leftPos + $imageWidth;

$page->drawImage($chompImage, $leftPos, $bottomPos, $rightPos, $topPos);

$style = new Zend_Pdf_Style();
$style->setLineColor(new Zend_Pdf_Color_RGB(0.9, 0, 0));
$style->setFillColor(new Zend_Pdf_Color_GrayScale(0.2));
$style->setLineWidth(3);
$style->setFont(
            new Zend_Pdf_Font_Standard(
                 Zend_Pdf_Const::FONT_HELVETICA_BOLD), 32);

$page->setStyle($style);

$page->drawText('Chomp! To go', $rightPos + 32, $topPos - 48);

$pdf->pages[0] = ($page);

$pdf->save("chomp.pdf");

?>
首先要创建样式对象并为其设置属性。线条的颜色在这里设置为 RGB 值(在 0 至 1 的范围上设置的红色、绿色和蓝色),这主要应用于图形,一会便可看到。填充色这里设置为深灰色,这也可以应用在图形中,但在本例中更重要的是设置文本的颜色。行宽属性决定了您的文本行的宽度。然后我们以点为单位设置字体和大小。PDF 文档中包含了 14 种标准的字体,并能够以 Zend_PDF_Const 值引用。这些字体包括: Zend_Pdf_Const::FONT_TIMES_ROMAN、Zend_Pdf_Const::FONT_TIMES_BOLD、Zend_Pdf_Const::FONT_TIMES_ITALIC、Zend_Pdf_Const::FONT_TIMES_BOLDITALIC 和同样具备这四个常量的 Helvetica 和 Courier,以及 Symbol 和 Zapf Dingbats 字体。

创建样式后,我们就能够为页面设置样式了。最后,我们准备好了将文本添加到页面中,并指定文本本身及其位置。在本例中,我们将文本放在距图片右侧半英寸,同时距页面顶部三分之二英寸的地方。

TOP

添加图形

样例页面的最后处理是其外部的边框。


清单 6. 添加图形
复制内容到剪贴板
代码:
...
$style->setFont(new
Zend_Pdf_Font_Standard(Zend_Pdf_Const::FONT_HELVETICA_BOLD), 32);

$page->setStyle($style);

$page->drawText('Chomp! To go', $rightPos + 32, $topPos - 48);

$page->drawRectangle(18, $pageHeight - 18, $pageWidth - 18,
                           18, Zend_Pdf_Const::SHAPEDRAW_STROKE);

$pdf->pages[0] = ($page);

$pdf->save("chomp.pdf");

?>
我们已经为组成这个矩形的线条设置了颜色和宽度,现在我们将左上角和右下角设至距页面边缘四分之一英寸的位置,并指定我们需要的只是边框线,而不是一个填充好的图形。我们有三种选择: Zend_Pdf_Const::SHAPEDRAW_FILLNSTROKE、Zend_Pdf_Const::SHAPEDRAW_STROKE 和 Zend_Pdf_Const::SHAPEDRAW_FILL。第一种选择包含了线和一种填充,所以如果我们在这里使用它,将会得到一个带红边的深灰色矩形。

让我们看一下应用程序中的真实实现情况。

TOP

与应用程序集成在一起

既然已经有了总体的想法,那么让我们来看一下将此功能与实际的应用程序相集成的情况。

为视图添加描述

由于想要这个 PDF 既显示已保存条目的标题又显示其描述,所以我们需要将这些描述保存到数据库中。此时,只有当用户选中 “全文” 选项时,信息才会保存到数据库中,在这种情况下,我们所看到的 HTML 页面并不是我们真正想要包含在 PDF 文档中的页面。

首先要向 viewChannel.php 添加真实的描述。


清单 7. 向 viewChannel.php 添加描述
复制内容到剪贴板
代码:
<html>
<head>
    <title><?php echo $this->escape($this->title);
?></title>
</head>
<body>
  [<a href='/'>Back to Main Menu</a>]<br>
  <h1><?php echo $this->escape($this->title); ?></h1>
  
  <table>
    <tr>
      <td>Channel Title (click to read)      
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
      <td>Save entry to database
        amp;nbsp;amp;nbsp;amp;nbsp;amp;nbsp;amp;nbsp;amp;nbsp;</td>
      <td>Save Full Text</td>
    </tr>
  <?php
     $feedTitle = $this->title;
     foreach ($this->rssFeed as $item) {
         $entryTitle = $item->title();
         $link = $item->link();
         $description = $item->description();
         echo "<form method='POST' action='/feed/saveEntry'>";
         echo "<input type='hidden' name='feedTitle'" .
              " value='$feedTitle'/>";
         echo "<input type='hidden' name='title' " .   
              " value='$entryTitle'/>";
         echo "<input type='hidden' name='link' value='$link'/>";
         echo "<input type='hidden' name='type' value='rssFeed'/>";
         echo "<input type='hidden' name='description' ".
              " value='".addslashes\
              (strip_tags($description))."'/>";
         echo "<tr><td><a
href='$link'>$entryTitle</a><br>";
         echo "$description</td>";
         echo "<td><input type='submit'
value='save'/></td>";
         echo "<td><input name='saveFullText' " .
              " type='checkbox'/></td></tr></form>";
     }
?>
  </table>
</body>
</html>
第一个变化是根据条目本身实际获得描述。完成这一操作后,要将该描述添加到两个地方。

第一个地方是一个隐藏的表单元素(这样,当用户提交表单来保存条目的时候就可以获取它)。为此,我们需要确保排除掉描述可能会使标记走样的任何可能。换言之,我们需要移除掉任何 HTML 标记并确保文本中的任何引号前都加了反斜杠,这样,就可以不带来任何不良影响地呈现描述了。为此,我们运用了 php strip_tags() 函数和 addslashes() 函数。

最后,为了方便用户,我们可以在页面中显示描述本身。在这种情况下,我们并不在意是否移除掉 HTML 标记,总之,这是因为我们要将其显示为 HTML 页面。

TOP

保存描述

既然已经将描述放入表单中,现在我们需要修改 saveEntryAction() 函数以便执行它。打开 FeedController.php 文件,做如下更改。


清单 8. 将描述保存到数据库中
复制内容到剪贴板
代码:
...
    public function saveEntryAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $filterPost = Zend::registry('fPost');
        $feedTitle = $filterPost->getRaw('feedTitle');
        $channelTitle = $filterPost->getRaw('title');
        $channelLink = $filterPost->getRaw('link');
        $type = $filterPost->getRaw('type');
        $saveFullText = $filterPost->getRaw('saveFullText');
        $description = $filterPost->getRaw('description');

        if($saveFullText){
            $http = new Zend_Http_Client($channelLink);
            $response = $http->get();
            if ($response->isSuccessful())
                $fullText = $response->getBody();
            else{
                echo 'Error occurred, full text not saved, please reload.';
                return;
            }
        }

        $db = Zend::registry('db');
        $row = array(
                     'Username' => $username,
                     'feedname' => $feedTitle,
                     'channelname' => $channelTitle,
                     'link' => $channelLink,
                     'entrysaved' => $saveFullText ? 'true' : 'false',
                     'entrydata' =>
                          $saveFullText ? $fullText : $description
                     );
        
        $table = 'savedentries';
        $rowsAffected = $db->insert($table, $row);

        if($type == 'webPage')
            $this->_redirect("/");
        else
            $this->_redirect("/feed/viewChannel?title=$feedTitle");
    }
...
这个过程非常简单明了:我们从表单中获取描述的值,然后用它们填充更新行数组的 entrydata 项。在这种情况下,只有当用户没有选中 fullText 选项时,才使用该值。

现在开始处理 PDF 本身。

TOP

创建新行为

从 MVC 的角度看,创建 PDF 到底是属于用户、索引还是属于提要的一部分,这很难定夺。此处,我们作出了一个武断的决定,将它作为提要的一部分。为此,将下面一个函数添加到 FeedController.php 文件中,如下所示。


清单 9. 将 createPdfAction() 添加到 FeedController.php
复制内容到剪贴板
代码:
...
    public function createPdfAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

    }
...
这只是一个可以从浏览器访问的简单函数。首先从注册库中重新获取会话,并使用该会话获得当前用户名。

TOP

添加已保存条目

让我们来了解一下如何获取那些最终将添加到 PDF 中的信息。


清单 10. 添加已保存条目
复制内容到剪贴板
代码:
...
public function createPdfAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $db = Zend::registry('db');
        $select = $db->select();
        $select->from('savedentries', '*');
        $select->where("username=?", $username);
        $sql = $select->__toString();
        $entries = $db->fetchAll($sql);
        foreach($entries as $row){
            $title = $row['feedname'];
            $entrydata = $row['entrydata'];
            if($row['channelname'] != '')
            {
                $title = "$title > " . $row['channelname'];
            }
            echo '<p>'.$title.'<br />';
            echo $entrydata.'</p>';

        }
    }
...
首先,为注册库恢复数据库连接,然后创建一个 select 语句,针对当前用户名检索 savedentries 表中所有行。执行该查询,为每个条目检索标题和数据,如果存在频道名,那么还要将频道名添加到标题中。

将浏览器指向 http://localhost/feed/createPdf 查看此函数的运行结果。

TOP

创建新文档

创建 PDF 的步骤与样例文档的类似。


清单 11. 创建新文档
复制内容到剪贴板
代码:
...
    public function createPdfAction()
    {

        require_once 'Zend/Pdf.php';

        $pdf = new Zend_Pdf();
        $page = new Zend_Pdf_Page(Zend_Pdf_Const::PAGESIZE_LETTER);

        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $db = Zend::registry('db');
        $select = $db->select();
            if($row['channelname'] != '')
            {
                $title = "$title > " . $row['channelname'];
            }
            echo '<p>'.$title.'<br />';
            echo $entrydata.'</p>';

        }
        $pdf->pages[0] = ($page);
    }
...
我们正在创建一个新的 PDF 文档以及一个单个页面,然后在完成全部处理后将这个页面添加到该文档中。

TOP

创建新文档

创建 PDF 的步骤与样例文档的类似。


清单 11. 创建新文档
复制内容到剪贴板
代码:
...
    public function createPdfAction()
    {

        require_once 'Zend/Pdf.php';

        $pdf = new Zend_Pdf();
        $page = new Zend_Pdf_Page(Zend_Pdf_Const::PAGESIZE_LETTER);

        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $db = Zend::registry('db');
        $select = $db->select();
            if($row['channelname'] != '')
            {
                $title = "$title > " . $row['channelname'];
            }
            echo '<p>'.$title.'<br />';
            echo $entrydata.'</p>';

        }
        $pdf->pages[0] = ($page);
    }
...
我们正在创建一个新的 PDF 文档以及一个单个页面,然后在完成全部处理后将这个页面添加到该文档中。

TOP

添加界面条目

我们应该对添加图像、文本和边框感到很熟悉。


清单 12. 添加 logo
复制内容到剪贴板
代码:
...
    public function createPdfAction()
    {

        require_once 'Zend/Pdf.php';

        $pdf = new Zend_Pdf();
        $page = new Zend_Pdf_Page(Zend_Pdf_Const::PAGESIZE_LETTER);
        $chompImage = new
                Zend_Pdf_Image_JPEG('E:\sw\public_html\chomp.jpg');

        $pageHeight = $page->getHeight();
        $pageWidth = $page->getWidth();
        $imageHeight = 72;
        $imageWidth = 72;

        $topPos = $pageHeight - 36;
        $leftPos = 36;
        $bottomPos = $topPos - $imageHeight;
        $rightPos = $leftPos + $imageWidth;

        $page->drawImage($chompImage, $leftPos, $bottomPos,
                                         $rightPos, $topPos);
   
        $style = new Zend_Pdf_Style();
        $style->setLineColor(new Zend_Pdf_Color_RGB(0.9, 0, 0));
        $style->setFillColor(new Zend_Pdf_Color_GrayScale(0.2));
        $style->setLineWidth(3);
        $style->setFont(new Zend_Pdf_Font_Standard(
                            Zend_Pdf_Const::FONT_HELVETICA_BOLD), 32);

        $page->setStyle($style);

        $page->drawText('Chomp! To go', $rightPos + 32, $topPos - 48);

        $page->drawRectangle(18, $pageHeight - 18, $pageWidth - 18,
                                18, Zend_Pdf_Const::SHAPEDRAW_STROKE);

        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $db = Zend::registry('db');
        $select = $db->select();
        $select->from('savedentries', '*');
...
请注意,在本例中,我们为图像设置了一个具体的位置,以方便应用程序进行寻找。

TOP

发新话题