Large data tables on MPDF - Can i avoid resize? - php

I'm generating some reports on my admin and I'm exporting to PDF using the MPDF library. But when this has a very large amount of data, MPDF is resizing the table to fit on page, so that it becomes awkward.
Is there any way to prevent this from happening? The code I am using to generate is the below. I've tried to set "shrink_tables_to_fit" to 0 and 1, both unsuccessful. Also tried to set autosize=1 to every table.
$stylesheet = file_get_contents($_SERVER['DOCUMENT_ROOT'].'/site/css/layout/pdf.css');
$bootstrap = file_get_contents('https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css');
$mpdf = new mPDF('pt', 'a4');
$mpdf->shrink_tables_to_fit = 1;
$mpdf->setAutoTopMargin = 'stretch';
$mpdf->setAutoBottomMargin = 'stretch';
$mpdf->SetHTMLHeader('<img src="'.$_SERVER['DOCUMENT_ROOT'].'/site/img/content/pdf/header.jpg">');
$mpdf->SetFooter('{DATE j/m/Y H:i}|{PAGENO}/{nb}');
$mpdf->WriteHTML($bootstrap, 1);
$mpdf->WriteHTML($stylesheet, 1);
$mpdf->WriteHTML($html);
$mpdf->Output($_SERVER['DOCUMENT_ROOT'].'/upload/consultas-pre/'.$nomeArquivo.'.pdf', 'F');

You can split the entire table into different tables and at end of the each table add :
<div style="page-break-after: always;"> </div>
Which will create a page break.
Example :
<?php $rows=1; foreach($allData as $data){ ?>
<table>
<tr>
<td>Your table data</td>
</tr>
</table>
<?php if($row == 10) { ?>
<div style="page-break-after: always;"> </div>
<?php } ?>
<?php } ?>
You can read more about mpdf page break here

I found it.
I can not have a table with other tables inside. When this happens, MPDF gets lost in resize.

I had the same issue. Tried everything I found on stackoverflow in different combinations: shrink_tables_to_fit, autosize="0", page-break-inside:..., overflow:..., etc...
In my case my table was inside the div that had position:absolute in its inline style. After I changed it to relative or removed position completely, the table is divided properly between pages.

Try this in the table opening tag:
<table autosize="1">

Related

Getting variables from SQL Server for mPDF

I'm using the mPDF class to output a pdf of data from a PHP file. I need to loop through a SQL Server query, save as new variables and write into the $html so it can be outputted to the pdf. I can't place it in the WriteHTML function because it does not recognize PHP code. I need the contents of the whole array so I can't just print one variable.
I have two files:
pdf-test.php:
This file gathers session variables from other php files that are included and reassigns them, so I can use them in the $html.
<?php
// Include files
require_once("form.php");
require_once("configuration.php");
session_start();
$html = '
<h3> Form A </h3>
<div>
<table>
<thead>
<tr>
<th colspan="3">1. Contact Information</th>
</tr>
</thead>
<tr>
<td> First Name: </td>
<td> Last Name: </td>
</tr>
<tr>
<td>'.$firstName.'</td>
<td>'.$lastName.'</td>
</tr>
.
.
.
</table>
';
echo $html;
pdf-make.php:
This file holds the code to actually convert the contents of pdf-test.php into a pdf.
<?php
// Direct to the mpdf file.
include('mpdf/mpdf.php');
// Collect all the content.
ob_start();
include "pdf-test.php";
$template = ob_get_contents();
ob_end_clean();
$mpdf=new mPDF();
$mpdf->WriteHTML($template);
// I: send the file inline to the browser.
$mpdf->Output('cust-form-a', 'I');
?>
This is my loop:
$tbl = "form_Customers";
$sql = "SELECT ROW_NUMBER() OVER(ORDER BY custFirt ASC)
AS RowNumber,
formID,
custFirt,
custLast,
displayRecord
FROM $tbl
WHERE formID = ? and displayRecord = ?";
$param = array($_SESSION["formid"], 'Y');
$stmt = sqlsrv_query($m_conn, $sql, $param);
$row = sqlsrv_fetch_array($stmt);
while ($row = sqlsrv_fetch_array($stmt)) {
$rowNum = $row['RowNumber'];
$firstN = $row['custFirt'];
$lastN = $row['custLast'];
}
When I try to include $rowNum, $firstN or $lastN in the $html such as
<td> '.$rowNum.'</td>
, it just shows up blank.
I'm not sure where the loop should go (which file) or how to include the $rowNum, $firstN and $lastN variables in the $html like the others.
I'm new to PHP (and relatively new to coding in general) and I don't have much experience working with it, but I've been able to make mPDF work for me in similar instances without the query included.
Any help would be greatly appreciated. Thank you so much!
I'm not sure how your loop interacts with the other two files, but this looks overly complex to me. I'd approach this in one .php file, something sort of like this:
<?php
//Include Files
include('mpdf/mpdf.php');
... //Your additional includes
//Define a row template string
$rowtemplate =<<<EOS
<tr>
<td>%%RowNumber%%</td>
<td>%%custFirt%%</td>
<td>%%custLast%%</td>
</tr>
EOS;
//Initialize the HTML for the document.
$html =<<<EOS
<h3> Form A </h3>
... //Your code
<td> Last Name: </td>
</tr>
EOS;
//Loop Code
$tbl = "form_Customers";
... //Your code
$row = sqlsrv_fetch_array($stmt);
while ($row = sqlsrv_fetch_array($stmt)) {
//Copy rowtemplate to a temporary variable
$out_tmp = $rowtemplate;
//Loop through your SQL variables and replace them when they appear in the template
foreach ($row as $key => $val) {
$out_tmp = str_ireplace('%%'.$key.'%%', $val, $out_tmp);
}
//Append the result to $html
$html .= $out_tmp;
}
// Close the open tags in $html
$html .= "</table></div>";
//Write the PDF
$mpdf=new mPDF();
$mpdf->WriteHTML($html);
$mpdf->Output('cust-form-a', 'I');
I'm using heredoc syntax for the strings, since I think this is the cleanest way to include a large string.
Also, I prefer to omit the closing ?> tag as it introduces a stupid source of errors.

HTML to PDF when TCPDF base on foreach and while

This might have been already answered, yes, I used Google, and the search here stackoverflow, but the problem remains...
I'm using TCPDF library to generate PDF file, it works just fine, but I have the following situation, in order for my to generate a PDF ot just the HTML I need to put a few foreach and while's and IF's inside the HTML so that I can get the layout that the user is requesting...
so...
if(isset($_POST['submit'])) {
$ids = htmlentities($_POST['id'], ENT_NOQUOTE;
$con = conectionDB();
$query 'SELECT * FROM books WHERE id = "$ids"';
$doit = $con->query($query);
// at this point everything is file
// a few if's and we are done with the fetching "books" data
// a few other tuff that is required from the library nothing fancy...
// now here is the hard part
// next line will build my layout to display my PDF
$build_HTML = <<<EOD
<style>
.clasess {...}
.nother {...}
</style>
<table width="100%" border="1" cellpadding="0" bordercolor="#333333" >
<tr>
<td>Author</td>
<td>Books</td>
</tr>
<tr>
<td> Jong </td>
<td>
<table>
<tr>
<td>Title</td><td>Year</td>
</tr>
// Here is the problem I need to put a query to fetch the related data from
// another table
$books = conectionDB();
$bookQ = "SELECT * FROM titles WHERE name = '$author_name'";
$doitTitles = $books->query($bookQ);
if ($doitTitles->num_row > 1) {
while($dos = $doitTitles->fetch_assoc()){
// my td's, but this doest work...
}
}
</table>
</td>
</tr>
</table>
EOD;
$pdf->writeHTML($build_HTML, true, false, false, false, '');
} else {
// Go back...
}
As you can see I need that query right there, you may have notice that I have $doitTitles->num_row > 1 why? because if there is more than 1 tittle the layout would different if there is only 1 record...
We know that, that wont work, so the question is, is there another way to do that?
now, before the user go to the PDF, I display the information in plain html, which mean that the HTML that I use before the user go in to the PDF will be the same... so I was wondering, there another library that I can use to render the HTML in to PDF instead of building the PDF from inside the file... the user is able to see the result so those result just put 'em in a PDF...
Thank you!
I have the solution, so the thing is that I'm trying to output an html that is been build with dynamic content, if that was to be show as any other page, my_page.html there is no problem, but in PDF using <<
$raw_html = 'Tables';
// a few while and foreach's and queries
$raw_html .='Closed tables';
// Ready the HTML
$html = <<<EOD
$raw_html
EOD;
$pdf->writeHTML($html, true, false, false, false, '');
$pdf->Output('pdf_file_with_dynamic_html.pdf', 'I');
// the end.
by preparing the html before the output it gives me the data I need the way i need it...
that solved my problem.

Generate an HTML table with dynamic row Height

I need to print an html table with row heights sets dynamically based on some values from the database using PHP. seems that html 5 doesn't support inline height and with tags and using css instead.
My requirement is to generate an html file and then convert it into pdf using DOM pdf.
Please guide me how to set these parameters dynamically inline or using css or whether a library already available for the same purpose.
I Googled a lot, but unable find any results matching my requirement.
Also am attaching final output format
(In answer column i printed some values which is the height required for each row)
Thanks in advance
You can use inline styles:
<tr style="height: 300px;"></tr>
I am not sure if you can effectively set the height of a <tr> tag, so you might have to set the height of each <td> in the row individually. Give it a try.
Furthermore, I am not sure how you have your array of rows and columns structured, but this might shed some light on how to do it.
<?php
$array=array(array(50,'r1c1','r1c2'),array(50,'r2c1','r2c2'));
echo '<table>';
foreach($array as $row)
{
echo '<tr style="height: '.$row[0].'px;">';
echo '</tr>';
for($i=1;$i<count($row);++$1)
{
echo '<td>'.$row[$i].'</td>';
}
}
echo '</table>';
?>
If you still need help, post the exact array you wish to turn into a <table> and I will do my best to assist.
If I understand this right, your table rows can be different sizes from each other, but for each row there is a rule in database, that sets row's height, no matter what height the content of the row, right? Then you can use something like this:
<html>
<head>
<style>
<?php foreach($yourRows as $key => $row) { ?>
#row<?=$key;?>{
height: <?=$row['height']; ?>px;
}
<?php } ?>
</style>
</head>
<body>
<table>
<?php foreach($yourRows as $key => $row) { ?>
<tr id="row<?=$key; ?>">
...
</tr>
<?php } ?>
</table>
</body>
In the style tag you can replace "#row<?=$key;?>" with "#row<?=$key;?> td"
Updated
Anyway, if you want to use the inline styling, you can make it happen like that:
<html>
<body>
<table>
<?php foreach($yourRows as $row) { ?>
<tr style="height:<?=$row['height']; ?>px">
Or you can apply height to td instead of the tr...
</tr>
<?php } ?>
</table>
</body>
If you think that jQuery might work here is a suggestion. I'm not sure it works with DOMPDF but as we're dynamically creating CSS it should be fine once the DOM has loaded.
If you know exactly the heights of each row - then select them using jQuery using eq.
$(document).ready(function() {
$('table tr').eq(1).css({'height':'250'});
$('table tr').eq(3).css({'height':'450'});
});
Here is the fiddle.
That way you don't have to modify the output but you have to make the assumption the content isn't going to be higher than your fixed height.
If you need this to be more dynamic then you'll need to either associate identifiers to your rows, like a class or something like that. Or alternatively, if you have a pattern in your content is to create a regular expression that scans your content and identifies it that way - then you can apply CSS rules to these rows once matched using jQuery.
EDIT
OK so I may have slightly misunderstood if you have the height value stored in the database. It also looks as though you've determined already that you're unable to use inline styles.
Here is my next suggestion.
You're building the table from a loop so it probably looks something like this.
foreach($rows as $row) {
echo '<tr data-height="'.$row['height'].'"><td>...</td></tr>;
}
if you add data-height="'.$row['height'].'" then you have a value that we can get using jQuery's data like so.
$(document).ready(function() {
$('table tr').each(function() {
var height = $(this).data('height');
$(this).css({ 'height': height });
});
});
Here is an example fiddle with static data-height values. Let me know how you get on.

PHP, random image code generation integrated with CSS

I had a piece of code which randomly selected an image for display on a website. That however was before I upgraded my HTML skills with CSS. Now I want to integrate the CSS into the PHP code.
I never was good with PHP, and only managed the random image generator because a friend did the coding for me. Now I have no idea how to integrate the required formatting characters from the CSS into the PHP.
Original Code:
<?php
$images = array("banner001.jpg", "banner002.jpg", "banner003.jpg",
"banner004.jpg", "banner005.jpg", "banner006.jpg", "banner007.jpg", "banner008.jpg",
"banner009.jpg", "banner010.jpg", "banner011.jpg", "banner012.jpg", "banner013.jpg",
"banner014.jpg", "banner015.jpg",); mt_srand((double)microtime() * 1000000);
$num = array_rand($images);
print("<img src=\"_pic-lib/banner-bg/".$images[$num]."\" alt=\"A random banner image\" class=\"bordered\" id=\"rightside\" />");
?>
Working HTML w/ CSS that does a single image:
<table id="banner"><tr><td id="banner" style="background-image:url('_pic-lib/banner-
bg/banner005.jpg')"><img src="_pic-lib/banner.png" alt="NCPAM GRS Banner" /></td>
</tr></table>
Hybrid code I have so far: (which doesn't work)
<table id="banner"><tr><?php $images = array("banner001.jpg", "banner002.jpg",
"banner003.jpg", "banner004.jpg", "banner005.jpg", "banner006.jpg", "banner007.jpg",
"banner008.jpg", "banner009.jpg", "banner010.jpg", "banner011.jpg", "banner012.jpg",
"banner013.jpg", "banner014.jpg", "banner015.jpg",); mt_srand((double)microtime() *
1000000); $num = array_rand($images); print("<td id=\"banner\" style=\"background-
image:url('_pic-lib/banner-bg/".$images[$num]. '\")" alt=\"A random banner image\"
/>");?></td></tr></table>
Website where code is to be used.
The picture on top is what the final result is supposed to be. The second picture is the randomly selected picture code, without the CSS integrated. The third image... which isn't there... is the final integrated version.
#
Actually figured it out. Turns out that the "View Page Source" in firefox is a decent code debugger. It has syntax highlighting which can be very helpful in diagnosing code errors.
<table id="banner2"><tr><?php $images = array("banner001.jpg", "banner002.jpg",
"banner003.jpg", "banner004.jpg", "banner005.jpg", "banner006.jpg", "banner007.jpg",
"banner008.jpg", "banner009.jpg", "banner010.jpg", "banner011.jpg", "banner012.jpg",
"banner013.jpg", "banner014.jpg", "banner015.jpg",); mt_srand((double)microtime() *
1000000); $num = array_rand($images); print("<td id=\"banner2\" style=\"background-
image:url('_pic-lib/banner-bg/$images[$num]')\" >");?><img src="_pic-lib/banner.png"
alt="NCPAM GRS Banner" /></td></tr></table>

TCPDF: HTML table and page breaks

I am creating a large HTML table and I have problem with page breaks as you can see in the following image:
Is there a method settle down the problem automatically? Or what is the way to do it?
Try adding this to your <tr> tags: nobr="true".
So a quick example would be:
<table>
<tr nobr="true">
<td>Test</td>
<td>Test 2</td>
</tr>
</table>
The nobr="true" prevents the table rows from ever breaking apart. You can also put this on <td> and <th> tags.
I know it's rather old question, but I had this same issue today, my table was splitted between pages and I investigated a little bit further on the method from FastTrack's answer and it turned out that you can also use the nobr="true" attribute also to the <table> tag. That is, for me such code solved this problem:
<table nobr="true">
<tr>
<td>Test</td>
<td>Test 2</td>
</tr>
</table>
Warning - this code makes sense only when your tables are smaller than one page.
I had the same problem with overlapping headers.
I tried yevgeny solution, but that required some more editions to my PDF generator code (I have lots of PDFs outputs written in FPDF and I wanted to minimize the process of miograting them to TCPDF), so I used this more simple solution
class MYPDF extenfs TCPDF {
//your initialization code
function header(){
//your code here
//we change only the top margin and this executes for every header in every page, even the frst one
$this->SetTopMargin($this->GetY());
}
}
roney, thank you so much, writing HTML generated overlaps with the header for pages 2,3 .. this worked for me:
class your_PDF extends TCPDF
{
var $top_margin = 20;
function Header() {
// set top margin to style pages 2, 3..
//title goes here
$this->top_margin = $this->GetY() + 5; // padding for second page
}
}
in your code
// set top margin to style pages 2, 3..
$pdf->SetMargins(15, $pdf->top_margin, 15);
For the interested, just do as follows and it will work like a charm:
$pdf->SetMargins(0, 0, 0);
$pdf->SetHeaderMargin(0);
$pdf->SetFooterMargin(0);
Strangely enough the solutions mentioned here didn't work for me. Well, it did sortof, but content inside of tags would get repeated (as desired) but would then cause layout issues for the cell above or below if it was rowspanned. As I experimented, it just got worse.
My solution, while inelegant, was to set AutoPageBreak to false, put a row incrementer counter up, incrementing for each row, then checked if it had exceeded a certain value. If so, I closed the table, used writeHTML(), called addPage() and then continued , having rebuilt it as a new table, headers and all.
Like I said, inelegant, but it worked. I hope this helps someone ... it's a fairly obvious solution but execution is not always quite so obvious. Also, there may be a better way that works for your particular situation, but if it doesn't, give mine a try. :)
Some CSS solved for me:
// Include the main TCPDF library (search for installation path).
require_once('tcpdf/tcpdf.php');
// create new PDF document
$pdf = new TCPDF('R', PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Author');
$pdf->SetTitle('TCPDF HTML Table');
$pdf->SetSubject('TCPDF Tutorial');
$pdf->SetKeywords('TCPDF, PDF, html,table, example, test, guide');
// set default header data
$pdf->SetHeaderData('', '', ' HTML table', '');
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
//$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(15, 15, 15);
$pdf->SetHeaderMargin(15);
$pdf->SetFooterMargin(15);
// set auto page breaks
$pdf->SetAutoPageBreak(TRUE, 15);
// set image scale factor
//$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// ---------------------------------------------------------
// set font
$pdf->SetFont('times', '', 10);
// add a page
$pdf->AddPage();
$start = 1;
$end = 254;
$step = 1;
$arr = range($start, $end, $step);
$table_header .= sprintf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>", 'IP', 'Computer', 'User', 'Fone');
foreach ($arr as $ar) {
$row[] = $ar;
}
foreach ($row as $r):
if (($r % 40) === 0):
$table_header;
endif;
$table .= sprintf("<tr>\n<td>%s</td>\n<td>%s</td>\n<td>%s</td>\n<td>%s</td>\n</tr>\n", $r, $r, $r, $r);
endforeach;
$now = date("d/m/Y");
$caption = "<caption>IP addresses <em>$now</em></caption>\n";
$n = "\n";
$tbl = <<<EOD
<style>
table{
font-family: serif;
font-size: 11pt;
}
table tr {
}
table tr td {
padding:3px;
border:#000000 solid 1px;
}
em {
font-size: 4pt;
}
tr { white-space:nowrap; }
</style>
<h1>{$caption}</h1>
{$table_begin}
{$table_header}
{$table}
</table>
EOD;
$pdf->writeHTML($tbl, true, false, false, false, '');
// reset pointer to the last page
//$pdf->lastPage();
// ---------------------------------------------------------
//Close and output PDF document
$pdf->Output('html_table.pdf', 'I');
//============================================================+
// END OF FILE
//============================================================+
have you tried
<table>
<thead>
<tr>
<td>
This is my header which appears on every page
</td>
</tr>
</thead>
<tr>
<td>
My Content
</td>
</tr>
</table>
I'm using smarty, with this you have more possibilities to manually break the table (e.g. if you're using borders). If needed, i'll post this, too...

Categories