HTML to PDF when TCPDF base on foreach and while - php

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.

Related

Large data tables on MPDF - Can i avoid resize?

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">

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.

Extracting data from HTML using Simple HTML DOM Parser

For a college project, I am creating a website with some back end algorithms and to test these in a demo environment I require a lot of fake data. To get this data I intend to scrape some sites. One of these sites is freelance.com.To extract the data I am using the Simple HTML DOM Parser but so far I have been unsuccessful in my efforts to actually get the data I need.
Here is an example of the HTML layout of the page I intend to scrape. The red boxes mark the required data.
Here is the code I have written so far after following some tutorials.
<?php
include "simple_html_dom.php";
// Create DOM from URL
$html = file_get_html('http://www.freelancer.com/jobs/Website-Design/1/');
//Get all data inside the <tr> of <table id="project_table">
foreach($html->find('table[id=project_table] tr') as $tr) {
foreach($tr->find('td[class=title-col]') as $t) {
//get the inner HTML
$data = $t->outertext;
echo $data;
}
}
?>
Hopefully someone can point me in the right direction as to how I can get this working.
Thanks.
The raw source code is different, that's why you're not getting the expected results...
You can check the raw source code using ctrl+u, the data are in table[id=project_table_static], and the cells td have no attributes, so, here's a working code to get all the URLs from the table:
$url = 'http://www.freelancer.com/jobs/Website-Design/1/';
// Create DOM from URL
$html = file_get_html($url);
//Get all data inside the <tr> of <table id="project_table">
foreach($html->find('table#project_table_static tbody tr') as $i=>$tr) {
// Skip the first empty element
if ($i==0) {
continue;
}
echo "<br/>\$i=".$i;
// get the first anchor
$anchor = $tr->find('a', 0);
echo " => ".$anchor->href;
}
// Clear dom object
$html->clear();
unset($html);
Demo

strip tags placing a delimiter or store to an array using PHP

I've stripped the tag data from an url like
$url='http://abcd.com';
$d=stripslashes(file_get_contents($url));
echo strip_tags($d);
but unfortunately all the tag values are clubbed together like user14036100 9.00user23034003 11.33user32028000 14.00 where in the user1, user2, user3 attributes are stored, It is hard to analyse the attribute values as all are joined together by strip_tags().
so friends can someone help me to strip each tag and store in an array or by placing a delimiter at the end of each stripped tag data.
Thanks in advance :)
You cannot achieve this with strip_tags(), since it justs removes the tags. You wan't to replace them with e.g. a whitespace character (new line, space, ..).
You should probably do this with a regex call, which just replaces all tags.
A better way would be to parse the fetched page with DOMDocument, so that you can derive the structure directly from the HTML structure.
Example of usage of DOMDocument
You have the following example html page:
<!DOCTYPE html>
<html>
<head>
<title>This is my title</title>
</head>
<body>
<table id="someDataHere">
<tr>
<th>Country</th>
<th>Population</th>
</tr>
<tr>
<td>Germany</td>
<td>81,779,600</td>
</tr>
<tr>
<td>Belgium</td>
<td>11,007,020</td>
</tr>
<tr>
<td>Netherlands</td>
<td>16,847,007</td>
</tr>
</table>
</body>
</html>
You can use DOMDocument to fetch the entries in the table:
$url = "...";
$dom = new DOMDocument("1.0", "UTF-8");
$dom->loadHTML(file_get_contents($url));
$preparedData = array();
$table = $dom->getElementById("someDataHere");
$tableRows = $table->getElementsByTagName('tr');
foreach ($tableRows as $tableRow)
{
$columns = $tableRow->getElementsByTagName('td');
// skip the header row of the table - it has no <td>, just <th>
if (0 == $columns->length)
{
continue;
}
$preparedData[ $columns->item(0)->nodeValue ] = $columns->item(1)->nodeValue;
}
$preparedData will now hold the following data:
Array
(
[Germany] => 81,779,600
[Belgium] => 11,007,020
[Netherlands] => 16,847,007
)
Some notes
Since you are developing a crawler (spider), you are highly dependent on the HTML structure of the target webpage. You may have to adjust your crawler every time they change something in their templates.
This is just a simple example, but it should make clear, how you can now use it, to produce more advanced results.
Since DOMDocument implements the DOM methods, you have to work your way through the HTML structure with the possibilities they provide.
For very huge HTML pages DOMDocument can become quite expensive in terms of memory.

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