voilab / tctable
快速高效地创建表格,无需HTML格式化,并具有高级分页选项
Requires
- php: >=5.5.0
- tecnickcom/tcpdf: 6.*
Requires (Dev)
- phpunit/phpunit: 4.*
- scrutinizer/ocular: ~1.1
README
安装
通过Composer
在项目根目录创建一个 composer.json 文件
{ "require": { "voilab/tctable": "1.*" } }
$ composer require voilab/tctable
使用方法
这个库的目标不是替换复杂的HTML表格,其中包含行和单元格跨度。它主要用于当你想要显示大量行(如发票等)并确保页面断行在正确位置时。你可以调整孤行数(必须出现在最后一页的最小行数),并使用插件来自定义流程(见下文)。
基本用法
$pdf = new \TCPDF(); $minRowHeight = 6; //mm $tctable = new \voilab\tctable\TcTable($pdf, $minRowHeight); $tctable->setColumns([ 'description' => [ 'isMultiLine' => true, 'header' => 'Description', 'width' => 100 // check inline documentation to see what options are available. // Basically, it's everything TCPDF Cell and MultiCell can eat. ], 'quantity' => [ 'header' => 'Quantity', 'width' => 20, 'align' => 'R' ], 'price' => [ 'header' => 'Price', 'width' => 20, 'align' => 'R' ] ]); // get rows data $rows = getMyDatasAsMyObjs(); // add a page so the content can be printed on something $pdf->AddPage(); // draw body $tctable->addBody($rows, function (\voilab\tctable\TcTable $table, \MyObj $row) { $change_rate = 0.8; // map row data to TcTable column definitions return [ 'description' => $row->getDescription(), 'quantity' => $row->getQuantity(), 'price' => $row->getPrice() * $change_rate ]; }); $pdf->Output('tctable.pdf', 'I');
插件
让列宽度适应剩余页面宽度
$tctable ->addPlugin(new \voilab\tctable\plugin\FitColumn('text')) ->addColumn('text', [ 'isMultiLine' => true, 'header' => 'Text' // no need to set width here, the plugin will calculate it for us, // depending on the other columns width ]);
条纹行
$tctable // set true to have the first line with colored background ->addPlugin(new \voilab\tctable\plugin\StripeRows(true));
孤行管理
// set the minimum elements you want to see on the last page (if any) $nb = 4; // set a footer margin. Useful when you have lot of lines, and a total as the // last one. If you want the total to appears at least with 4 lines, you have // to add to the pageBreakTrigger margin this line height: the footer $mFooter = 10; // i.e: mm $tctable->addPlugin(new \voilab\tctable\plugin\Widows($nb, $mFooter));
调试
TcTable 随附一个调试插件工具,可以显示每个事件传递的数据。
// create the plugin. You can define which events to listen (default to rowadd, // rowadded, rowskipped, headeradd, headeradded, pageadd and pageadded) and the // printer object (default to an HTML output with <pre>) $debug = new \voilab\tctable\plugin\Debug(); $debug ->setBounds($fromIndex = 0, $numberOfRows = 2, $dieWhenOutOfBounds = true); // $dieWhenOutOfBounds will stop the script with die(). Usefull for quick // debug $tctable->addPlugin($debug);
你可以通过创建自己的来扩展打印机对象
class MyDebugPrinter implements \voilab\tctable\plugin\debug\PrinterInterface { public function output(TcTable $table, array $data) { // do something, log, etc. } } // ... create an instance of debug plugin // you can set printer the way below, or via the 2nd argument in plugin // constructor. $debug->setPrinter(new MyDebugPrinter());
高级插件:在每页末尾为列绘制小计
我们可以进一步计算列的总和,并在页面末尾显示当前总和,最后在下一页报告。
<?php namespace your\namespace; use voilab\tctable\TcTable; use voilab\tctable\Plugin; class Report implements Plugin { // column on which we sum its value private $column; // the current calculated sum private $currentSum = 0; public function __construct($column) { $this->column = $column; } public function configure(TcTable $table) { $table // when launching the main process, reset sum at 0 ->on(TcTable::EV_BODY_ADD, [$this, 'resetSum']) // after each added row, add the value to the sum ->on(TcTable::EV_ROW_ADDED, [$this, 'makeSum']) // when a page is added, draw the "subtotal" string ->on(TcTable::EV_PAGE_ADD, [$this, 'addSubTotal']) // after a page is added, draw the "sum from previous page" string ->on(TcTable::EV_PAGE_ADDED, [$this, 'addReport']); } public function resetSum() { $this->currentSum = 0; } public function makeSum(TcTable $table, $data) { $this->currentSum += $data[$this->column] * 1; } public function getSum() { return $this->currentSum; } public function addSubTotal(TcTable $table) { $pdf = $table->getPdf(); $pdf->SetFont('', 'I'); $pdf->SetTextColor(150, 150, 150); $pdf->Cell($table->getColumnWidthUntil($this->column), $table->getColumnHeight(), "Sub-total:", false, false, 'R'); $pdf->Cell($table->getColumnWidth($this->column), $table->getColumnHeight(), $this->currentSum, false, false, 'R'); $pdf->SetTextColor(0, 0, 0); $pdf->SetFont('', ''); } public function addReport(TcTable $table) { $pdf = $table->getPdf(); $pdf->SetFont('', 'I'); $pdf->SetTextColor(150, 150, 150); $table->getPdf()->Cell($table->getColumnWidthUntil($this->column), $table->getColumnHeight(), "Sum from previous page", 'B', false, 'R'); $table->getPdf()->Cell($table->getColumnWidth($this->column), $table->getColumnHeight(), $this->currentSum, 'B', true, 'R'); $pdf->SetTextColor(0, 0, 0); $pdf->SetFont('', ''); } }
以及TcTable
$tctable->addPlugin(new \your\namespace\Report('total'));
自定义事件
TcTable 触发一些我们可以监听的事件。插件大量使用它们。但你可以简单地定义事件,而不需要插件。这允许我们添加一些有用的方法。
在每一页上添加标题
use \voilab\tctable\TcTable; // ... create tctable $tctable // when a page is added, draw headers ->on(TcTable::EV_PAGE_ADDED, function(TcTable $t) { $t->addHeader(); }) // just before headers are drawn, set font style to bold ->on(TcTable::EV_HEADER_ADD, function(TcTable $t) { $t->getPdf()->SetFont('', 'B'); }) // after headers are drawn, reset the font style ->on(TcTable::EV_HEADER_ADDED, function(TcTable $t) { $t->getPdf()->SetFont('', ''); });
渲染器和主体函数
在解析数据时,你可以为每个列或某些列定义渲染函数,或者在调用 $tctable->addBody() 时使用匿名函数。这些函数被调用两次,因为在高度计算过程中需要。在某些情况下,你需要考虑这一点。
$total = 0; $tctable->addBody($rows, function (TcTable $t, $row, $index, $isHeightCalculation) use (&$total) { // if $height is true, it means this method is called when height // calculation is running. If we want to do a sum, we check first if // $isHeightCalculation is false, so it means the func is called during row // draw. if (!$isHeightCalculation) { $total += $row->getSomeValue(); } // you still need to return the data regardless of $isHeightCalculation return [ 'description' => $row->getDescription(), 'value' => $row->getSomeValue() ]; }); echo sprintf('Total: %d', $total);
相同的概念也适用于列渲染器。
注意 在上述情况(创建总和)中,你最好使用插件或事件。使用事件 TcTable::EV_ROW_ADDED,你可以完全做到同样的事情,而无需担心高度计算(见下文)。
$total = 0; $tctable ->on(TcTable::EV_ROW_ADDED, function (TcTable $t, $row) use (&$total) { $total += $row->getSomeValue(); }) ->addBody($rows, function (TcTable $t, $row) { return [ 'description' => $row->getDescription(), 'value' => $row->getSomeValue() ]; }); echo sprintf('Total: %d', $total);
优化
如果你确切知道每行的高度,你可以通过这种方式优化工作流程,从而绕过高度计算
$tctable->on(TcTable::EV_ROW_HEIGHT_GET, function (TcTable $t, $row, $index) { return 6.4; // or null for default calculation });
如果你想更改单元格高度的计算方式,你可以通过这种方式覆盖默认行为
$tctable->on(TcTable::EV_CELL_HEIGHT_GET, function (TcTable $t, $column, $data, $row) { if ($column == 'specialColumn') { return 12.8; } // use default calculation return null; });
请记住,只有多行单元格会被检查其高度。其他单元格在过程中不被考虑。
自定义绘图函数
如果你需要插入图像或需要对单元格的绘图进行非常具体的事情,你可以通过这种方式绕过正常的绘图函数
$tctable->addColumn('specialColumn', [ 'header' => "Special column", 'width' => 15, 'drawFn' => function (TcTable $t, $data, array $definition, $column, $row) { $t->getPdf()->Image(); //configure TCPDF Image method your way // return false to draw the cell normally, if there's no image to // display, for example }, 'drawHeaderFn' => function (TcTable $t, $data, array $definition, $column, $row) { // same comments as above } ]);
测试
尚未编写测试...
$ phpunit
安全性
如果你发现任何与安全相关的问题,请使用问题跟踪器。
致谢
许可证
MIT许可证(MIT)。请参阅许可证文件以获取更多信息。