Adam Smith
Adam Smith

Reputation: 511

Is it possible to make a TCPDF element with maximum height?

I'm working with TCPDF using PHP. I'm trying to make a PDF document that must be only one page long. This document includes a table, the data of which has a dynamically determined number of rows.

What I'd like to do is to give this table a maximum height (e.g. 10 cm), and to shrink the font size of the table if it goes over this limit. That way the data is still there, but the document will fit on the one page. Is this possible? I've tried using the WriteHTML() method, but it seems to ignore the height I give it. (That is, if the data goes over, it just keeps writing rather than cutting it off.)

Is this possible?

Upvotes: 0

Views: 5060

Answers (1)

EPB
EPB

Reputation: 4029

One way to do this would be to set the size of individual MultiCell or Cell outputs. There's a lot of visual tweaking involved, but you can get pretty precise. I created a rough example with fixed line heights for simplicity's sake. Basically the idea would be to set your maximum height and place (Multi)Cells to the right of the previous one with the $ln parameter set to 0. (Or set the coordinates before each call.) You can see an example output file here: https://s3.amazonaws.com/rrbits/maxheight.pdf

MultiCell has an autofit setting that'll handle scaling the fonts down for you, which I use in the MultiCell example. A potential caveat is that it will allow wrapping.

Cell doesn't have an autofit parameter, but you can simulate it by setting the font size to a sane maximum, check the cell string's width with GetStringWidth and reducing the font size until it fits. [Edit: I don't do it here, but it'd be a good idea to restore the font size after outputting your cell or you could have some unexpected results.] (see the loop on line 105). (Cell does have some font stretching options available, see TCPDF example 004, but they didn't quite do what you were asking.)

<?php
require_once('TCPDF-6.2.17/tcpdf.php');
// create new PDF document
$pdf = new TCPDF('L', 'mm', array(130,130), true, 'UTF-8', false);

$pdf->SetFont('times', '', 8);
$pdf->SetAutoPageBreak(TRUE, 5);

//Generating a random table for testing.
$table = [
  ['Name','Description','md5'],
];
$rows = rand(10,30);
//$rows = rand(3,5);
for($i = 0; $i < $rows; $i++) {
  $table[] = array(
    'Sector '.rand(1,10000),
    str_repeat('An Apple', rand(2,6)),
    md5(rand(1,100000)),
  );
}

$pdf->addPage();

$pdf->Write(2, 'MultiCell Example');
$pdf->Ln();
$pdf->SetFont('times', '', 8);

$column_widths = array(
  20,
  60,
  30,
);

//Total table should only be 10cm tall.
$maxheight = 100/count($table);
if($maxheight > 10) {
  $maxheight = 10;
}
foreach($table as $index => $row) {
  if($index == 0) {
    $pdf->SetFillColor(230,230,255);
  } else {
    if( ($index&1) == 0 ) {
      $pdf->SetFillColor(210,210,210);
    } else {
      $pdf->SetFillColor(255,255,255);
    }
  }
  $pdf->SetX(10);
  $currenty = $pdf->GetY();
  foreach($row as $index => $column) {
    $pdf->MultiCell(
      $width = $column_widths[$index],
      $minheight = $maxheight,
      $text = $column,
      $border = 'B', //Border bottom
      $align = 'L',
      $fill = true,
      $ln = 0, //Move to right after cell.
      $x = null,
      $y = null,
      $reseth = true,
      $stretch = 0,
      $ishtml = false,
      $autopadding = true,
      $maxheight,
      $valign = 'T',
      $fitcell = true);
  }
  $pdf->SetY($currenty + $maxheight);
}

$pdf->addPage();

$pdf->SetFont('times', '', 8);
$pdf->Write(2, 'Cell Example');
$pdf->Ln();
$pdf->SetFont('times', '', 8);

$maxheight = 100/count($table);
if($maxheight > 8) {
  $maxheight = 8;
}

$maxfontsize = 10;
$pdf->SetFontSize($maxfontsize);

foreach($table as $index => $row) {
  if($index == 0) {
    $pdf->SetFillColor(230,230,255);
  } else {
    if( ($index&1) == 0 ) {
      $pdf->SetFillColor(210,210,210);
    } else {
      $pdf->SetFillColor(255,255,255);
    }
  }
  $pdf->SetX(10);
  $currenty = $pdf->GetY();
  foreach($row as $index => $column) {
    //Reduce the font size to fit properly.
    $pdf->SetFontSize($csize = $maxfontsize);
    //0.2 step down font until it fits the cell.
    while($pdf->GetStringWidth($column) > $column_widths[$index]-1 ) {
      $pdf->SetFontSize($csize -= 0.2);
    }
    $pdf->Cell(
      $width = $column_widths[$index],
      $cellheight = $maxheight,
      $text = $column,
      $border = 'B', //Border bottom
      $ln = 0,
      $align = 'L',
      $fill = true,
      $stretch = 1,
      $ignore_min_height = true,
      $calign = 'T',
      $valign = 'T');
  }
  $pdf->SetY($currenty + $maxheight);
}

$pdf->Output(dirname(__FILE__).'/maxheight.pdf', 'F');

Addendum: Alternative method with WriteHTML

A way you could do this with WriteHTML is to start a transaction with startTransaction, set a base font for the entire table and write the HTML, then check the number of pages. If you encounter an automatic page break, roll back the transaction and try with a smaller font size. Otherwise commit the transaction.

I've updated the link above with the output from this as an example:

//Example with WriteHTML.
$pdf->AddPage();
$pdf->SetFont('times', '', 8);
$pdf->Write(2, 'WriteHTML Example');
$pdf->Ln();
$pdf->SetFont('times', '', 8);

//Max height of 100 mm.
$maxy = $pdf->GetY()+100;
$fontsize = 14;
//Make table markup.
$tablehtml = '<table cellspacing="2" style="font-family: times; font-size:_FONTSIZE_px;">';
foreach($table as $index => $row) {
  if($index == 0) {
    $rowstyle = ' background-color: rgb(230,230,255); '.
      'font-size: 110%; font-familt: times; font-weight: bold;'.
      'border-bottom: 1px solid black;';
  } else {
    if( ($index&1) == 0 ) {
      $rowstyle = 'background-color: rgb(210,210,210);';
    } else {
      $rowstyle = 'background-color: white;';
    }
  }
  $tablehtml .= "<tr style=\"$rowstyle\">";
  foreach($row as $column) {
    $tablehtml .= "<td>$column</td>";
  }
  $tablehtml .= '</tr>';
}
$tablehtml .= '</table>';

//Transaction loop.
$numpages = $pdf->getNumPages();
$done = false;
while(!$done) {
  $pdf->startTransaction(true);
  $pdf->SetFont('times', '', 14);
  $outtable = str_replace('_FONTSIZE_', $fontsize, $tablehtml);
  $pdf->writeHTML($outtable);
  if($pdf->getNumPages() > $numpages || $pdf->GetY() > $maxy) {
    //If we encountered a page break or exceeded the desired maximum height
    //rollback the transaction.
    $pdf->rollbackTransaction(true);
    $fontsize -= 0.4;
    //Larger font steppings will be less precise, but faster.
  } else {
    $pdf->commitTransaction(true);
    $done = true;
  }
}

Upvotes: 1

Related Questions