内容


使用 TCPDF 动态创建 PDF

将您的网页变为完美的印刷品

Comments

简介

TCPDF 是托管在 Sourceforge.net 上最活跃的项目之一,其完全在 PHP 上实现了强大的 PDF 生成引擎。这使得其更容易安装,即使在您无法访问系统目录或编译自己的代码的站点上。同时,通过让您直接查看 PHP 代码生成的结果,而不使用任何中间步骤,这使迭代开发更加容易。

TCPDF 支持一系列有用的图像格式,包括 SVG 矢量格式和位图格式,如 JPEG 和 PNG。一个简单独立的实用工具让您可以处理 TrueType、OpenType、PostScript Type 1 和 CID-0 字体,使它们可以添加到 TCPDF 创建的文档。您可以使用 TCPDF 来生成无数 1-D 和 2-D 条形码格式,且它支持所有常见的 PDF 功能,如书签、文档链接、压缩、注释、文档加密和数字签名。

用 PHP 编写 TCPDF 并使用其页面,这使其易于创建并部署 PDF 生成的 Web 页面。在您使用任何支持 Web 服务器和您最喜欢的 PHP 开发环境开发并部署 TCPDF 时,我将使用如下工具:

  • Eclipse V3.5.2 — 我最新欢的开源开发环境之一,其支持广泛的编程语言和环境。
  • PHP Development Tools V2.2.0 — 适用于 Eclipse 的 PHP 插件。
  • MAMP Pro V1.9 — 适用于 Mac OS X 的方便的程序包,其通过有用的 GUI 前端在一个隔离的环境中提供 Apache、MySQL 和 PHP。虽然 Mac OS X 附带安装 Apache 和 PHP,但我还是选择使用此工具,因为其提供了一系列稳定且容易分离的 Web 服务器/数据库/PHP。
  • TCPDF V5.0.006 — TCPDF 当前的稳定版本。

您可以在 参考资料 部分找到以上所有工具的下载链接。

如果您已经安装了 PHP,我们就来看看如何在您自己的网站上使用 TCPDF。我们将检查安装过程,然后我们将使用 PHP 生成一个显示可能来自任何电子商务站点的发票式样(invoice-style)文档的网页。此后,我们将使用 TCPDF 来创建一个使用类似格式的可打印的 PDF 版发票。

安装 TCPDF

当您从 Sourceforge.net 下载 TCPDF 时,它提供一个自包含的 ZIP 存档,也就是说,您可以使用您最喜欢的 ZIP 提取工具来解压存档,您最终将获得一个包含您所需要的所有信息的 TCPDF 目录。

如果您将 TCPDF 目录添加到您的 web 文档目录,则您可以通过加载 doc/index.html 访问 TCPDF 文档并通过加载 examples/index.php 文件查看任何示例,这也可在 TCPDF 网站上找到(请参考 参考资料)。

然而在您可以查看示例以前,您需要配置您的 TCPDF 安装。

配置 — 类 UNIX 系统

如果您正在类 UNIX® 系统上安装 TCPDF,则您需要更改文件模式,因为它们并没有全部被标记为可执行。在 Microsoft® Windows® 系统上存在一个创建 TCPDF 存档的副作用。幸运的是,很容易在来自 shell 的一个失衡中调整这些(请参考 清单 1)。您还需要确保缓存和图像目录是可写入的,因为 TCPDF 将在那里存储临时文件。

下一步,您需要将文件分配给用户和组 web 服务器;虽然这通常是用户 www 和组 www,但是这将取决于您的系统。如果您正在您的个人网站领域以外运行 TCPDF,(在您的主目录下通常是 public_html),则您可以跳过此步骤。

清单 1. 调整文件模式和所有权
$ cd tcpdf
$ find . -type f | xargs chmod -x
$ chmod +w cache images
$ chown -R www:www .

请注意命令可能使用 . 代替 : 来在一些系统上分离用户和组;如果出现问题,则检查其文档详细资料。

适用于每个人的配置

使用您最喜欢的文本编辑器,加载 config/tcpdf_config.php 文件。这就是 TCPDF 的配置设置并让您控制库的默认设置,并告诉它如何找到自己的支持文件。

在 tcpdf_config.php 中您想要变更的设置包括:

  • PDF_PAGE_FORMAT— 如果您不使用公制页面格式,则设置其为 letter。除非您手头有一些非常奇特方法,否则 TCPDF 可能比您的打印机支持更大的页面尺寸。
  • PDF_UNIT— 如果您想用点而不是毫米来布置您的 PDF 文档,则设置其为 pt
  • PDF_CREATORPDF_AUTHOR— 一旦您的 PDF 生成代码忘了设置它们,则默认文档创建者和作者。

您可以在您的 PHP 代码中调整这些设置中的任意一个,如果无法确定也无需担心(例如,如果您在此处设置的是像模式文档布局,您仍然可以创建景观文档)。

假设您没有移动文件和目录远离其初始位置,则通过默认配置设置,TCPDF 应该没有呈现任何其附带示例的问题。

在这一点上,您可以加载示例文件以确保您拥有一个正在运行的 TCPDF 安装。让我们使用它来创建一个可打印版的发票。

创建发票

在您创建了电子商务网站以后,您的客户将订购一些物品,而您将收取他们的货款。虽然这很不错,但是他们将想得到某种发票,以防订单错误或他们的信用卡公司搞混了付款,。

让我们制作一个合理美观的发票网页,这样他们可以看到他们已经订购了什么和您将要向他们收取什么。

第一个版本 — Web 页面

使用我最喜欢的 PHP 开发环境,我已经创建了一个新的发票文件夹,其包含如下文件:

  • Invoice.php — PHP 生成发票
  • Invoice.css — CSS 式样的发票
  • Azuresol_OnyxTree-S.png — Azuresol 的 “OnyxTree S”,您将其用作您公司的标志(来自 iconfinder.com Web 站点的免费图标(请参考 参考资料))
  • gentleface_print.png — gentleface 的 “Print”,您将使用其触发 PDF 生成(来自 iconfinder.com 的另一个免费图标)

在 Invoice.php 内部,检查看看是否使用 PDF 参数调用页面(请参考 清单 2)。如果不是,用户想要一个正常 web 页面,这样您就可以使用 generateHTML 函数(请参考 清单 3)显示页面。稍后您将看到 generatePDF 函数,但是可以肯定使用它来生成发票数据的 PDF 没任何问题。

Listing 2. 页面生成控制代码
if( array_key_exists( 'PDF', $_REQUEST ) ) {
    generatePDF( $invoiceData );
} else {
    generateHTML( $invoiceData );
}

$invoiceData 阵列拥有所有发票数据;虽然您将直接在 Invoice.php 上创建它,但是您可以设想数据来自于数据库、web 服务或某种网上购物车系统。

清单 3. 生成 HTML
function generateHTML( $data ) {
?>
... HTML code was here ...
<?php
    foreach( $data['items'] as $item ) {
        echo '<tr class="invoiceRow">' . "\n";
        echo '    <td class="itemCol">' . $item[0] . "</td>\n";
        echo '    <td class="quantityCol">' . $item[1] . "</td>\n";
        echo '    <td class="priceCol">' . $item[2] . "</td>\n";
        echo '    <td class="costCol">' . $item[3]. "</td>\n";
        echo "</tr>\n";
    }
?>
... HTML code was here ...
<?php
    echo '<td class="totalCol">' . $data['total'] . "</td>\n";
?>
... HTML code was here ...
<?php
    echo 'Invoice prepared for ' . $data['user'] . ' on ' . $data['date'] . "\n";
?>
... HTML code was here ...
<?php
    
}

为了简单明了我已经删掉了此处所有的 HTML 标记代码;您可以在位于 CreatingPDFs-Invoice.zip 存档文件中的 Invoice.php 上看到它, CreatingPDFs-Invoice.zip 存档文件可以在 下载 部分找到。

使用传递给函数的发票信息作为 $data,创建一个包含每个项目一行的表、及其数量、单价和总价的表。在表的末尾,添加一个包含订单的总成本的行。页脚包含用户 ID 和发票生成的日期/时间。

当然,简单明了的事情,以及您在自己站点上实现它的时候所面临的主要挑战,将与您的数据源互动,并获得恰到好处的 CSS 式样。

图 1. 光彩夺目的网页发票
截图显示了一个有吸引力的可打印发票示例;Sumatra Special 的最大数量为 24,而其他的则处于 1 到 5 之间
截图显示了一个有吸引力的可打印发票示例;Sumatra Special 的最大数量为 24,而其他的则处于 1 到 5 之间

我完全虚构了一家提供优良咖啡和茶的 South Seas Pacifica 公司,显然我对 Sumatran 品种深深的吸引了。至少他们的价格很吸引人!

右下角的打印机图标是诱人的,您希望能够有单击那里并获得打印发票。这里就是 TCPDF 的用武之地。并不只是打印网页,您将为我们尊贵的客户提供一个时髦的 PDF 版本,这会使他们在轻松的状态下完成打印。

第二个版本 — PDF

现在,您的网页发票看起来很好,您需要创建可以打印的 PDF 版本。

回忆 清单 2,将 PDF 参数 (http://.../Invoice.php?PDF) 与 Invoice.php 页面一起加载将调用具有相同数据的 generatePDF 函数(请参考清单 4)。

清单 4. 生成 PDF 发票
function generatePDF( $data ) {
    # Create a new PDF document.
    $pdf = new InvoicePdf( $data, 'P', 'pt', 'LETTER' );

    # Generate the invoice.
    $pdf->CreateInvoice();
    
    # Output the PDF document.
    $pdf->Output( 'Your_Invoice.pdf', 'D' );
}

generatePDF 函数创建了一个 InvoicePdf 对象,调用其 CreateInvoice 方法,然后发送一个名为 Your_Invoice.pdf 的下载 PDF 文档给用户的浏览器。虽然这看起来非常简单,但是隐藏了必须自己创建 InvoicePdf 类的事实,且需要复制原始发票式样。让我们来看看这是如何进行的。

InvoicePdf 类扩展了 TCPDF 的 TCPDF 类,这是主 PDF 生成引擎。在构造函数(请参考 清单 5)中,使用传入的 $orientation(页面方向)、$unit(计量单位)和 $format(页面大小)初始化父类。TCPDF 构造函数的最后三个参数表明输入的是 Unicode(默认为 Ture),已生成 PDF 的字符编码格式是 UTF-8,且您不应使用磁盘缓存 (False)。虽然在创建 PDF 时,磁盘缓存减少了内存占用,但是速度变得更慢了。因为我们的文件不大也不复杂,所以无需权衡这一点。

在初始化父类以后,将参考存储到发票数据以便今后使用。下一步,使用 SetMargins 方法将页边距设置为宽度 72 点、高度 36 点。SetMargins 最终参数表明您正在用自己的值来重写默认页边距。使用 SetAutoPageBreaks 方法,可在从底部算起有 36 点高时告知 TCPDF 自动创建新页面。

清单 5. InvoicePdf 类构造函数
function __construct( $data, $orientation, $unit, $format ) {
    parent::__construct( $orientation, $unit, $format, true, 'UTF-8', false );

    $this->invoiceData = $data;

    # Set the page margins: 72pt on each side, 36pt on top/bottom.
    $this->SetMargins( 72, 36, 72, true );
    $this->SetAutoPageBreak( true, 36 );
    
    # Set document meta-information
    $this->SetCreator( PDF_CREATOR );
    $this->SetAuthor( 'Chris Herborth (chrish@pobox.com)' );
    $this->SetTitle( 'Invoice for ' . $this->invoiceData['user'] );
    $this->SetSubject( "A simple invoice example for 'Creating PDFs on 
the fly with TCPDF' on IBM's developerWorks" );
    $this->SetKeywords( 'PHP, sample, invoice, PDF, TCPDF' );

    //set image scale factor
    $this->setImageScale(PDF_IMAGE_SCALE_RATIO); 
    
    //set some language-dependent strings
    global $l;
    $this->setLanguageArray($l);
}

在建立边距以后,设置 PDF 文档元信息。这将显示在您最喜欢的 PDF 查看器中的文档属性窗口。这些仅仅是字符串,因此您可以将它们设置为任何对您的应用程序有意义的信息。

使用 setImageScale 方法,在 TCPDF 的配置文件中使用默认图像缩放比例设置。这是用于将位图图像的大小从像素大小调整为适合的页面。

最后,使用 setLanguageArray 来为 PDF 文件设置一些语言相关的字符串;通过 TCPDF 主配置,这些会在特定语言配置文件中定义。

清单 6 中,您重写了 Header 方法,其被调用以便生成每一个页面的标题内容。首先,定义一些变量。以 14 点的 bigFont 大小开始,您会计算出商标图像的相对大小和相对于 bigFont 大小的正常文本大小。然后您将深入 TCPDF 调用。

ImagePngAlpha 方法插入放置在其左上角宽 72 点、高 32 点的商标图像,其与您以前的边距设置相匹配。因为它是一个正方形的图像,所以您可以指定相同的宽度和高度(已计算的 $imageScale)。您将要说明它是一个 PNG 图像,因为此调用实际可以插入 PNG 和 JPEG 图像(如果在您的 PHP 安装中已安装了 GD 库,则其也可以加载任何由 GD 支持的图像)。下一步,指定一个空值,因为您没有为此图像添加 PDF 链接(通过 AddLink 方法创建)目标。再下一步,使用 T 来说明您想在图像区域的右上角制定下一个 PDF 对象。最后,告知 TCPDF 不要调整图像大小,既保持初始的 72 dpi(一个通用的屏幕分辨率),且图像在页面上应该左对齐。

显然,ImagePngAlpha 方法对如何在 PDF 文档中将图像添加到页面的问题上给予您大量的控制(请参考清单 6)。

清单 6. 页面标题的生成
public function Header() {
    global $webcolor;

    # The image is this much larger than the company name text.
    $bigFont = 14;
    $imageScale = ( 128.0 / 26.0 ) * $bigFont;
    $smallFont = ( 16.0 / 26.0 ) * $bigFont;

    $this->ImagePngAlpha('Azuresol_OnyxTree-S.png', 72, 36, $imageScale, 
$imageScale, 'PNG', null, 'T', false, 72, 'L' );
    $this->SetFont('times', 'b', $bigFont );
    $this->Cell( 0, 0, 'South Seas Pacifica', 0, 1 );
    $this->SetFont('times', 'i', $smallFont );
    $this->Cell( $imageScale );
    $this->Cell( 0, 0, '', 0, 1 );
    $this->Cell( $imageScale );
    $this->Cell( 0, 0, '31337 Docks Avenue,', 0, 1 );
    $this->Cell( $imageScale );
    $this->Cell( 0, 0, 'Toronto, Ontario', 0, 1 );

    $this->SetY( 1.5 * 72, true );
    $this->SetLineStyle( array( 'width' => 2, 'color' => 
array( $webcolor['black'] ) ) );
    $this->Line( 72, 36 + $imageScale, $this->getPageWidth() - 72, 36 
+ $imageScale );
}

在标题上放置商标图像以后,设置字体(粗体 Times,使用您的 bigFont 大小),然后创建一些单元格来存放公司的名称和地址信息。这些单元格在 HTML 上包含文本,而且有点像表格单元格。Cell 方法采用这些参数(实际上更多;参考 TCPDF 文档获取完整列表):

  • Width— 单元格宽度;如果设置为 0,则单元格一直扩展到右侧边距(或者如果您使用的是从右到左的语言,则扩展到左侧边距)。
  • Height— 单元格高度;如果设置为 0,则单元格高度将扩展,以便能放得下内容。
  • Text— 该文本在单元格内绘制,使用当前的字体和颜色设置。
  • Border— 说明是否边界应该根据单元格制定。在这种情况下,由于您正在使用 0,所以您无需任何边界。您也可以传递 1 来根据单元格制定完整的边界,或字符串以便说明特定的边界。
  • Position— 说明何处创建下一个单元格;1 表明您需要其在下一行的开始出显示,但是您可以使用 0 在此单元格旁边添加下一个单元格,或者 2 以停留在当前的 X 坐标并移到下一行。

最后,我们的 Header 方法在标题的底部绘制了一条两点黑线,一直穿过页面的内容区域。图 2 显示了将如何在页面上查看,如 Mac OS X 的 预览应用程序所呈现的那样。

图 2. 打印标题,就像网页标题
截图对左边的地址显示了带有图形商标的标题,名称使用大的、粗体的字体,且地址使用斜体
截图对左边的地址显示了带有图形商标的标题,名称使用大的、粗体的字体,且地址使用斜体

现在您已经创建了一个原始标题的合理副本,您也需要重写 Footer 方法,以便于您生成页脚。这非常简单,只包含用户 ID 和发票信息,其通过另外一个两点黑线从剩余的页面中分离出来。

您尚未看到此方法的唯一部分正在使用使用一个负值调用 SetY 方法。在您这样做的时候,相对于页面的底部设置当前的 Y。在这里,您将要为页面页脚留下大量的空间,以确保您的绘制不太接近底部边距(请参考清单 7)。

清单 7. 页面页脚的生成
public function Footer() {
    global $webcolor;

    $this->SetLineStyle( array( 'width' => 2, 'color' => 
array( $webcolor['black'] ) ) );
    $this->Line( 72, $this->getPageHeight() - 1.5 * 72 - 2, 
$this->getPageWidth() - 72, $this->getPageHeight() - 1.5 * 72 - 2 );
    $this->SetFont( 'times', '', 8 );
    $this->SetY( -1.5 * 72, true );
    $this->Cell( 72, 0, 'Invoice prepared for ' . 
$this->invoiceData['user'] . ' on ' . $this->invoiceData['date'] );
}

在您创建页面时,显示出来的就像发票的网络版,只是减去了打印机的图标。您已经中断了此操作,因为这是打印版本(它是多余的)。图 3 得出了结果。

图 3. 已打印的页脚
截图显示了页脚,其对带有收件人、时间和日期的详细信息用粗体水平线标出
截图显示了页脚,其对带有收件人、时间和日期的详细信息用粗体水平线标出

在这一点上,您拥有附带美观的页眉和页脚的空白页。您需要为此添加实际的发票内容,这样做很有用。

在使用 AddPage 方法启动新的页面以后(在这种情况下,这是唯一的页面),将字体设置为 11 点 Helvetica 并将插入点从页面的顶部移动到 144 点处。在表开始以前,这会在页眉以下给我们留出一个小空间。

下一步,要计算您将需要使表居中的缩进量,这基于页面宽度、两个 72 点边距、一个宽列和三个正常列。

在此之后,您将使用以前计算出的值并描绘出每个单元格的完全边界,来创建一系列单元格以呈现列标题。您还要右对齐数字列的标题因为它们通过值进行排列。在标题单元格的最后,您要调用 Ln 方法以便向下移动到下一行的开始。

通过在发票上迭代项目的 foreach 循环,您为每一个内容行进行同类型的布局(请参考清单 8)。

清单 8. 页面内容的生成
public function CreateInvoice() {
    $this->AddPage();
    $this->SetFont( 'helvetica', '', 11 );
    $this->SetY( 144, true );

    # Table parameters
    #
    # Column size, wide (description) column, table indent, row height.
    $col = 72;
    $wideCol = 3 * $col;
    $indent = ( $this->getPageWidth() - 2 * 72 - $wideCol - 3 * $col ) / 2;
    $line = 18;

    # Table header
    $this->SetFont( '', 'b' );
    $this->Cell( $indent );
    $this->Cell( $wideCol, $line, 'Item', 1, 0, 'L' );
    $this->Cell( $col, $line, 'Quantity', 1, 0, 'R' );
    $this->Cell( $col, $line, 'Price', 1, 0, 'R' );
    $this->Cell( $col, $line, 'Cost', 1, 0, 'R' );
    $this->Ln();

    # Table content rows
    $this->SetFont( '', '' );
    foreach( $this->invoiceData['items'] as $item ) {
        $this->Cell( $indent );
        $this->Cell( $wideCol, $line, $item[0], 1, 0, 'L' );
        $this->Cell( $col, $line, $item[1], 1, 0, 'R' );
        $this->Cell( $col, $line, $item[2], 1, 0, 'R' );
        $this->Cell( $col, $line, $item[3], 1, 0, 'R' );
        $this->Ln();
    }

    # Table Total row
    $this->SetFont( '', 'b' );
    $this->Cell( $indent );
    $this->Cell( $wideCol + $col * 2, $line, 'Total:', 1, 0, 'R' );
    $this->SetFont( '', '' );
    $this->Cell( $col, $line, $this->invoiceData['total'], 1, 0, 'R' );
}

代码的最后一位呈现总行数。这演示了您如何可以更容易地在单元格之间变更字体式样(通过调用 SetFont 方法来打开或关闭粗体)。第一个文本单元格的宽度被设置横跨表的前三列,因为您需要发票总数出现在最后一列的底部。

一旦您完成后,发票项目表看起来将非常的棒(请参考图 4)。

图 4. 发票上的项目
附带数量、价格和总数的项目格式化清单的截图
附带数量、价格和总数的项目格式化清单的截图

通过逻辑地布局文本单元格,您已经用适合打印的格式重新创建了初始的网页。在无需变更网页本身或任何基础数据的情况下,TCPDF 让您将创建 PDF 的支持添加到您现存的 PHP 网页上。

结束语

本文向您介绍了 TCPDF,它是一种用于生成 PDF 文档的流行 PHP 库。TCPDF 无需额外的库就能执行此操作,并使其作为您现有 PHP 网站的一部分易于安装。本文向您显示了在类 UNIX 系统中安装并配置 TCPDF 的概述。然后您创建了一个简单的基于 Web 的发票,类似于您可能在一家从事异国热饮的电子商务站点上所看到的诸如此类的事情。

一旦您拥有一个看上去像专业发票的网页,您就可以扩展 TCPDF 类来生产一个 PDF 版本的发票,即客户可以轻松地下载或打印。重写 HeaderFooter 方法让我们用相似的方式建立页面,然后您可以编写一个附加方法来布置发票项目作为一个表。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=521215
ArticleTitle=使用 TCPDF 动态创建 PDF
publish-date=09132010