18 tables - marc rochkind

11
Generating PDFs with PHP and FPDF (and TCPDF) 71 18 Tables The easiest way to construct a table of data is with Cell, as shown in this example which shows the series number, date, time, building, and room number of panels from the Conference on World Affairs, held each April (since 1948) at the University of Colorado: $in = fopen('cwa_panels.csv', 'r'); $pdf = new FPDF('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(20, 20); $pdf->SetAutoPageBreak(true, 20); $widths = array(60, 50, 40, 90, 40); $pdf->AddPage(); while ($row = fgetcsv($in)) { for ($i = 0; $i < 5; $i++) $pdf->Cell($widths[$i], 10, iconv('UTF-8', 'windows-1252', $row[$i]), 1); $pdf->Ln(); } $pdf->Output();

Upload: others

Post on 11-Feb-2022

4 views

Category:

Documents


0 download

TRANSCRIPT

Generating PDFs with PHP and FPDF (and TCPDF) 71

18 Tables

The easiest way to construct a table of data is with Cell, as shown in this example which shows the series number, date, time, building, and room number of panels from the Conference on World Affairs, held each April (since 1948) at the University of Colorado:

$in = fopen('cwa_panels.csv', 'r'); $pdf = new FPDF('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(20, 20); $pdf->SetAutoPageBreak(true, 20); $widths = array(60, 50, 40, 90, 40); $pdf->AddPage(); while ($row = fgetcsv($in)) { for ($i = 0; $i < 5; $i++) $pdf->Cell($widths[$i], 10, iconv('UTF-8', 'windows-1252', $row[$i]), 1); $pdf->Ln(); } $pdf->Output();

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 72

(Only the first pages of PDFs are shown in this chapter.)

If you add another column for panel title, however, the table doesn’t work because the title doesn’t fit, nor can the line be lengthened to make it fit:

$in = fopen('cwa_panels.csv', 'r'); $pdf = new FPDF('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(20, 20); $pdf->SetAutoPageBreak(true, 20); $widths = array(60, 50, 40, 90, 40, 180); $pdf->AddPage(); while ($row = fgetcsv($in)) { for ($i = 0; $i < 6; $i++) $pdf->Cell($widths[$i], 10, iconv('UTF-8', 'windows-1252', $row[$i]), 1); $pdf->Ln(); } $pdf->Output();

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 73

The solution is to use MultiCell instead of Cell to get automatic line wrapping:

$in = fopen('cwa_panels.csv', 'r'); $top_margin = 20; $left_margin = 30; $bottom_margin = 20; $pdf = new FPDF('L', 'pt', array(300, 500)); $pdf->SetY(-1); // negative arg means relative to bottom of page $page_height = $pdf->GetY() + 1; $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins($left_margin, $top_margin); $pdf->SetAutoPageBreak(false); $widths = array(70, 50, 50, 70, 40, 140); $line_height = 8; $row_height = 2 * $line_height; $y = $page_height; // force an initial AddPage while ($row = fgetcsv($in)) { $x = $left_margin; $y += $row_height; if ($y > $page_height - $bottom_margin - $row_height) { $pdf->AddPage(); $y = $pdf->GetY(); } for ($i = 0; $i < 6; $i++) { $pdf->SetXY($x, $y); $pdf->MultiCell($widths[$i], $line_height, iconv('UTF-8', 'windows-1252', $row[$i]), 1, 'L'); $x += $widths[$i]; } } $pdf->Output();

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 74

Note in this example that AddPage was called explicitly when the next row would fail to fit, rather relying on automatic page breaking, which is too hard to handle when adjacent MultiCells need to put placed at the top of each page.

In the output, notice that, while all the text fits, some rows are higher than they need to be. If any panel titles were even longer, so that they took up three or more lines, even more vertical space would be wasted. A much worse problem is that the borders are drawn around each group of multicells, instead of neatly around each row.

The fix for the line-drawing problem is to outline each cell ourselves, with a rectangle height equal to the row height, rather than letting the call to MultiCell do it:

$in = fopen('cwa_panels.csv', 'r'); $top_margin = 20; $left_margin = 30; $bottom_margin = 20; $pdf = new FPDF('L', 'pt', array(300, 500)); $pdf->SetY(-1); // negative arg means relative to bottom of page $page_height = $pdf->GetY() + 1; $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins($left_margin, $top_margin); $pdf->SetAutoPageBreak(false); $widths = array(70, 50, 50, 70, 40, 140); $line_height = 8; $row_height = 2 * $line_height; $y = $page_height; // force an initial AddPage while ($row = fgetcsv($in)) {

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 75

$x = $left_margin; $y += $row_height; if ($y > $page_height - $bottom_margin - $row_height) { $pdf->AddPage(); $y = $pdf->GetY(); } for ($i = 0; $i < 6; $i++) { $pdf->SetXY($x, $y); $pdf->Rect($x, $y, $widths[$i], $row_height); $pdf->MultiCell($widths[$i], $line_height, iconv('UTF-8', 'windows-1252', $row[$i]), 0, 'L'); $x += $widths[$i]; } } $pdf->Output();

To avoid wasted space, we can use a nifty script, called “Table with MultiCells”, that Olivier Plathey, the developer of FPDF, has contributed to the FPDF scripts page at fpdf.org. It calculates the height of each row, dependent on the text in that row, and spaces the table appropriately. You precede the row output with calls to set up the column widths and alignments:

$pdf->SetWidths($widths_array) $pdf->SetAligns($alignments_array)

For example:

$pdf->SetWidths(array(25, 18, 18, 25, 14, 49)); $pdf->SetAligns(array('R', 'C', 'C', 'C', 'C', 'L'));

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 76

Then you call the function Row for each row of data. Note that Olivier has packaged his script into a class called PDF_MC_Table, and that it only works in millimeter units (we’ll see why shortly):

$in = fopen('cwa_panels.csv', 'r'); $pdf = new PDF_MC_Table('L', 'mm', array(106, 177)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(11, 7); $pdf->SetAutoPageBreak(true, 7); $pdf->AddPage(); $pdf->SetWidths(array(25, 18, 18, 25, 14, 49)); $pdf->SetAligns(array('R', 'C', 'C', 'C', 'C', 'L')); while ($row = fgetcsv($in)) $pdf->Row($row); $pdf->Output();

Here’s the actual Row function so you can see how it was done. Note that it uses a handy function NbLines, also in the class PDF_MC_Table, that calculates the height of a column of text.

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 77

function Row($data) // Identical to "Table with MultiCells" { // (Olivier, 2002-11-17) but reformatted //Calculate the height of the row $nb = 0; for ($i = 0; $i < count($data); $i++) $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); $h = 5 * $nb; //Issue a page break first if needed $this->CheckPageBreak($h); //Draw the cells of the row for ($i = 0; $i < count($data); $i++) { $w = $this->widths[$i]; $a = isset($this->aligns[$i]) ? $this->aligns[$i] : 'L'; //Save the current position $x = $this->GetX(); $y = $this->GetY(); //Draw the border $this->Rect($x, $y, $w, $h); //Print the text $this->MultiCell($w, 5, $data[$i], 0, $a); //Put the position to the right of the cell $this->SetXY($x + $w, $y); } //Go to the next line $this->Ln($h); }

While the table looks pretty good, notice that the line spacing of the title in the third row is too great. This is because the class uses a fixed cell height of 5, which only works even as well as it does for units of millimeters. When we change to points we get a mess because the cell height is too narrow:

$in = fopen('cwa_panels.csv', 'r'); $pdf = new PDF_MC_Table('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(30, 20); $pdf->SetAutoPageBreak(true, 20); $pdf->AddPage(); $pdf->SetWidths(array(70, 50, 50, 70, 40, 140)); $pdf->SetAligns(array('R', 'C', 'C', 'C', 'C', 'L')); while ($row = fgetcsv($in)) $pdf->Row(array($row[0], $row[1], $row[2], $row[3], $row[4], iconv('UTF-8', 'windows-1252', $row[5]))); $pdf->Output();

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 78

I’ve improved the Row function to fix this by using the public, but undocumented, variable FontSize to get the font size in current units, and also to add variable vertical padding and column styles:

function RowX($data) { $nb = 0; for ($i = 0; $i < count($data); $i++) { if (isset($this->styles[$i])) $this->SetFont('', $this->styles[$i]); $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); } $h = $this->FontSize * $nb + 2 * $this->vertical_padding; $this->CheckPageBreak($h); $x = $this->GetX(); $y = $this->GetY(); for ($i = 0; $i < count($data); $i++) { $w = $this->widths[$i]; $a = isset($this->aligns[$i]) ? $this->aligns[$i] : 'L'; if (isset($this->styles[$i])) $this->SetFont('', $this->styles[$i]); $this->Rect($x, $y, $w, $h); $this->SetXY($x, $y + $this->vertical_padding); $this->MultiCell($w, $this->FontSize, $data[$i], 0, $a); $x += $w; } $this->SetY($y + $h); }

You may want to add this function and the three that follow to your copy of the PDF_MC_Table class that you downloaded from the FPDF scripts page.

Here’s the previous example with “Row” changed to “RowX”:

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 79

$in = fopen('cwa_panels.csv', 'r'); $pdf = new PDF_MC_Table('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(30, 20); $pdf->SetAutoPageBreak(true, 20); $pdf->AddPage(); $pdf->SetWidths(array(70, 50, 50, 70, 40, 140)); $pdf->SetAligns(array('R', 'C', 'C', 'C', 'C', 'L')); while ($row = fgetcsv($in)) $pdf->RowX($row); $pdf->Output();

The spacing is much improved, but without some vertical padding it still looks too cramped. That can be fixed with calls to two functions I added:

function SetHorizontalPadding($cMargin) { // added by MJR $this->cMargin = $cMargin; } function SetVerticalPadding($vertical_padding) { // added by MJR $this->vertical_padding = $vertical_padding; }

MultiCell already has 1mm of horizontal padding built into it; SetHorizontalPadding overrides that (in current units, or course).

There’s also an added function to set the font style for each column. Any missing columns from the end take as their style the last one specified.

function SetStyles($styles_array) { // added by MJR

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.

Generating PDFs with PHP and FPDF (and TCPDF) 80

$this->styles = $styles_array; }

Here are the two functions used to improve the spacing and add some styles:

$in = fopen('cwa_panels.csv', 'r'); $pdf = new PDF_MC_Table('L', 'pt', array(300, 500)); $pdf->SetFont('Helvetica', '' , 7); $pdf->SetLineWidth(.1); $pdf->SetMargins(30, 20); $pdf->SetAutoPageBreak(true, 20); $pdf->SetHorizontalPadding(2); $pdf->SetVerticalPadding(3); $pdf->SetStyles(array('', 'B', 'B', '', '', 'I')); $pdf->AddPage(); $pdf->SetWidths(array(70, 50, 50, 70, 40, 140)); $pdf->SetAligns(array('R', 'C', 'C', 'C', 'C', 'L')); while ($row = fgetcsv($in)) $pdf->RowX($row); $pdf->Output();

This is a pretty good-looking table, much better than any of the earlier attempts.

All of the examples here work much the same way with TCPDF, with these changes:

• Change the class PDF_MC_Table so it extends TCPDF instead of FPDF.

• In the class, comment out the definition of the function

© 2013 by Marc J. Rochkind. All rights reserved. Buy the book at basepath.com/PDFbook.