Generating pdf from XML response taking so much time - php

I have xml in my database and wanted to generate pdf from xml, but if xml is too big then it through timeout error. I don't want to change my server timeout instead of how to improve the timing of pdf generate.
I am using <pre> tag for good formatting.
Below code for pdf generate
public function generateCreditReport($form, $path=false, $htmlPath=false){
$this->load->library('fpdf/fpdf');
$this->load->library('fpdi/fpdi');
if(!empty($path) && !file_exists($path)){
$data['form'] = $form;
$data['date'] = $data['xml'] = null;
if($data['form']->credit_report != ''){
try{
$data['xml'] = new SimpleXMLElement($data['form']->credit_report);
}catch(Exception $e){
}
$timeStatus = $this->admin->get_timestamp($data['form']->id, 'credit_pulled');
if(!empty($timeStatus)) {
$data['date'] = $timeStatus->date;
}
}
$pdf = new TCPDF();
$pdf->SetFont('Helvetica');
$pdf->SetFontSize(10);
$pdf->SetTextColor(0, 0, 0);
$pdf->SetProtection(array('print','modify'),"$form->loan_id","sefinance",0);
$pdf->AddPage();
$html = $this->load->view('admin/credit_report_pdf', $data,true);
$generatedReport = file_put_contents($htmlPath, $html , FILE_APPEND | LOCK_EX);
$pdf->WriteHTML($html);
if($path){
$pdf->Output($path, "F");
}else{
$pdf->Output();
}
}
}
View file credit_report_pdf is as below
<h4>Report Results</h4>
<br>
<?php if(!empty($xml)){ ?>
<?php echo print_credit($xml->EfxReport->USPrintImage); ?>
<?php } ?>
I am formatting using <pre> to look good.
function print_credit($report) {
$split = " <div style='page-break-before: always;'></div>" . repeater(' ', 43);
$report = preg_replace('/\* \* \*[\s\S]+?USER REF./', $split, $report);
$output = "";
$output = '<pre>';
$length = strlen($report);
$count = $length / 81;
$position = 1;
for ($i = 0; $i < $count; $i++) {
if (strpos(substr($report, $position, 80), 'THIS FORM PRODUCED BY EQUIFAX') === FALSE) {
$output .= substr($report, $position, 80) . '<br>';
}
if ($i == 32) {
$output .= '</pre><pre>';
}
$position += 81;
}
$output .= '</pre>';
return $output;
}
Here is screenshot how pdf looks like.
Please help me to improve the timing of pdf, currently taking more than 10min for 10-page pdf.

Related

Updating array data to a CSV file in php

I want to load csv file data to extract the urls from CSV and check for the title tag for all the urls and update the urls with corresponding title tags in a new csv. But while I try to add data to the csv all the urls are getting listed but only the title of the last url is displayed in the CSV. I have tried different ways to overcome this problem but unable to do so.
Here is my code:
<?php
ini_set('max_execution_time', '300'); //300 seconds = 5 minutes
ini_set('max_execution_time', '0');
include('simple_html_dom.php');
// if (isset($_POST['resurl'])) {
// $url = $_POST['resurl'];
if (($csv_file = fopen("old.csv", "r", 'a')) !== FALSE) {
$arraydata = array();
while (($read_data = fgetcsv($csv_file, 1000, ",")) !== FALSE) {
$column_count = count($read_data);
for ($c = 0; $c < $column_count; $c++) {
array_push($arraydata, $read_data[$c]);
}
}
fclose($csv_file);
}
$title = [];
foreach ($arraydata as $ad) {
$ard = [];
$ard = $ad;
$html = file_get_html($ard);
if ($html) {
$title = $html->find('title', 0)->plaintext;
// echo '<pre>';
// print_r($title);
}
}
$ncsv = fopen("updated.csv", "a");
$head = "Url,Title";
fwrite($ncsv, "\n" . $head);
foreach ($arraydata as $value) {
// $ar[]=$value;
$csvdata = "$value,$title";
fwrite($ncsv, "\n" . $csvdata);
}
fclose($ncsv);
I've changed the code so that you write the CSV file as you read the HTML pages. This saves having another loop and an extra array of titles.
I've also changed it to use fputcsv to write the data out as it sorts ot things like escaping values etc.
// Open file, using w to clear the old file down
$ncsv = fopen('updated.csv', 'w');
$head = 'Url,Title';
fwrite($ncsv, "Url,Title" . PHP_EOL . $head);
foreach ($arraydata as $ad) {
$html = file_get_html($ad);
// Fetch title, or set to blank if html is not loaded
if ($html) {
$title = $html->find('title', 0)->plaintext;
} else {
$title = '';
}
// Write record out
fputcsv($ncsv, [$value, $title]);
}
fclose($ncsv);
I was able to solve it finally.
Here is the updated code:
<?php
ini_set('max_execution_time', '300'); //300 seconds = 5 minutes
ini_set('max_execution_time', '0');
include('simple_html_dom.php');
// if (isset($_POST['resurl'])) {
// $url = $_POST['resurl'];
if (($csv_file = fopen("ntsurl.csv", "r", 'a')) !== FALSE) {
$arraydata = array();
while (($read_data = fgetcsv($csv_file, 1000, ",")) !== FALSE) {
$column_count = count($read_data);
for ($c = 0; $c < $column_count; $c++) {
array_push($arraydata, $read_data[$c]);
}
}
fclose($csv_file);
}
// print_r($arraydata);
$title=[];
$ncsv=fopen("ntsnew.csv","a");
$head="Website Url,title";
fwrite($ncsv,"\n".$head);
foreach($arraydata as $ad)
{
$ard = [];
$ard = $ad;
$html = file_get_html($ard);
if ($html) {
$title = $html->find('title', 0)->plaintext;
echo '<pre>';
print_r($title);
$csvdata="$ard,$title ";
fwrite($ncsv,"\n".$csvdata);
}
}
// fclose($ncsv);

PHP file unable to extract and split image from database after made changes to the file

I have this issue on one of servers, Up until last week, the feed it was pulling the content from was working fine. Now suddenly since last few days, when I made the change to extract category field from database since then it is not extracting the image from the feed but is able to extract all of other content. (This server was set up by previous developer).
I keep getting this error:
going to get file 2020-07-23T15:41:05
going to put /var/www/SpanishMix/
!! problem getting remote file ( 2020-07-23T15:41:05 ) in checkNGet ** trying replace , digiv/2105318.jpg
This is the code in php file:
<?php
set_time_limit(90);
ini_set('memory_limit', '128M');
$xurl = 'feedlink.com/feed/getXML.php';
$locs = 'server ip';
$locvid = '/var/www/SpanishMix/';
function decrypto($inStr)
{
$key = '';
$encrypted = $inStr;
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($encrypted), MCRYPT_MODE_CBC, md5(md5($key))), "\0");
return $decrypted;
}
function dis($v)
{
echo "<pre>\n";
print_r($v);
echo "\n</pre>\n<hr>\n";
}
// --------------------- grab XML
$c = file_get_contents($xurl);
if (strlen($c) < 100) {
exit('couldnt get data from CMS');
}
// --------------------- parse into usable array of objects
$sa = array();
$ta1 = explode('_ENDI_', $c);
foreach ($ta1 as $i) {
$ta2 = explode('_ENDF_', $i);
if ((strlen($ta2[0]) > 4) && (strlen($i) > 200)) {
$to = new stdClass();
$to->uid = $ta2[0];
$to->vurl = "";
$to->body = $ta2[1];
$to->people = trim($ta2[2]);
$to->headline = trim($ta2[3]);
$to->category = trim($ta2[4]);
$to->abstract = trim($ta2[5]);
$to->pubdate = trim($ta2[7]);
$to->storyid = trim($ta2[6]);
$to->iurl = trim($ta2[8]);
$tmpia = explode('.com/', $to->iurl);
$to->cpi = $tmpia[1];
$to->iloc = 'http://' . $locs . '/SpanishMix/' . $tmpia[1];
// code for durability date
$tmpda = explode('-', $to->pubdate);
$oldy = $tmpda[0];
$newy = $oldy + 1;
$newys = str_replace($oldy, $newy, $to->pubdate);
$to->durdate = $newys;
$to->gotv = 0;
$to->fs = 0;
if ($to->uid > 10000) {
$sa[$to->uid] = $to;
}
}
}
dis($sa);
// --------------------- scan vids dir, parse into array including filesize
$va = array();
$dir = $locvid . "*.jpg";
foreach (glob($dir) as $file) {
$tv = new stdClass();
$tv->fn = $file;
$tv->s = filesize($file);
array_push($va, $tv);
}
dis($va);
// --------------------- check through stories array structure marking those already with video
$vtg = '';
$itg = '';
$uidtg = '';
// loop through each story
foreach ($sa as $s) {
$found = 0;
foreach ($va as $v) {
// hack out matchable filename from video and story arrays
$tfn = '/var/www/SpanishMix/' . $s->cpi;
if ($tfn == $v->fn) {
$found = 1;
}
}
// if outer looop variable says no video found for this story, make this storey's video URL next to get
if (!$found) {
echo "<br>setting itg to $s->iurl<br>\n";
$itg = $s->iurl;
$uidtg = $s->uid;
}
}
echo "<hr><h1> getting video part</h1><br><br>";
// --------------------- elect first story entry with no file
if ($itg) {
// split img url to take
$ifa = explode('.com/', $itg);
$itg = $itg;
$outfile = '/var/www/SpanishMix/' . $ifa[1];
echo "\n<br><b>going to get file $itg<br>going to put $outfile</b><br>\n";
$remoteFile = file_get_contents($itg);
if (!$remoteFile) {
echo "!! problem getting remote file ( $itg ) in checkNGet\n\n";
} else {
$res = file_put_contents($outfile, $remoteFile);
if ($res) {
echo "put remote image file ( $outfile ) success !\n\n\n";
// NOW COPY LOCAL FILE TO FUNNY FILENAME
$cpifn = '/var/www/SpanishMix/digiv/' . $uidtg . '.jpg';
echo "* about to copy $outfile to $cpifn\n\n";
copy($outfile, $cpifn);
} else {
echo "put remote image file ( $outfile ) fail :( \n\n\n<br><br>";
}
}
}
$tt = '';
$tm = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<videonews>
<news>
<name>BBCC**storyid**</name>
<title>**headline**</title>
<subtitle>**abstract**</subtitle>
<text>**body**</text>
<keywords>**people**</keywords>
<date>**pubdate**</date>
<durability>**durdate**</durability>
<category>**category**</category>
<subcategory>Famosos</subcategory>
<image>
<file format="image">**iloc**</file>
</image>
</news>
</videonews>
';
$tb = '';
$out = $tt;
foreach ($sa as $s) {
if ($s->uid == $uidtg) {
$tfn = "$locs/SpanishMix/" . $tfna[3];
$tmpt = str_replace('**body**', $s->body, $tm);
//$tmpt = str_replace('**vidfs**', $s->fs, $tmpt);
$tmpt = str_replace('**headline**', $s->headline, $tmpt);
$tmpt = str_replace('**people**', $s->people, $tmpt);
$tmpt = str_replace('**abstract**', $s->abstract, $tmpt);
$tmpt = str_replace('**storyid**', $s->storyid, $tmpt);
$tmpt = str_replace('**category**', $s->category, $tmpt);
$tmpt = str_replace('**pubdate**', $s->pubdate, $tmpt);
$tmpt = str_replace('**durdate**', $s->durdate, $tmpt);
$tmpt = str_replace('**iloc**', $s->iloc, $tmpt);
$copyA2 = explode('SpanishMix/', $s->iloc);
$copyImageFN = $copyA2[1];
$copyNewImageFN = 'digiv/' . $uidtg . '.jpg';
echo "\n ** trying replace $copyImageFN, $copyNewImageFN\n";
$tmpt = str_replace($copyImageFN, $copyNewImageFN, $tmpt);
$out .= "$tmpt\n\n";
}
}
$out .= $tb;
if ($uidtg) {
echo "</pre><TEXTAREA cols='120' rows='80'>$out</TEXTAREA>\n";
echo "<hr>";
$ox = '/var/www/SpanishMix/digiv/' . $uidtg . '.xml';
$written = file_put_contents($ox, $out);
echo "\nALSO putting : xml to $ox [", $written, "]<br>\n";
} else {
echo "\n NO UIDTG [", $uidtg, "] ! \n not putting any files";
}
I would really appreciate some help on this.

excatonline php contact list won't show all complete information from the created user account

I tried to get data of "Contact" but i can't get these tags "AddressLine, City, FaxNumber, MiddelInitial, Note, State, ZipCode"
When i looked in ExactOnline i saw that under "Contact" Voornaam, Tussenvoegsel, Achternaam, Functienaam, Telefoon, Mobiel, E-mail and these are the thing I only get a output of (if its filled in).
Then I looked further and I saw "Relatie" over here are the parts i want but can't call it.
How can i fix this?
================================
Exactonline
Connection
================================
try {
//Code
for($i = 0; $i <1;) {
//AddressLine2
for($o = 0; $o <1;) {
//BusinessPhone
for($p = 0; $p <1;) {
//City
for($q = 0; $q <1;) {
//Country
for($w = 0; $w <1;) {
//Created
for($e = 0; $e <1;) {
//Email
for($r = 0; $r <1;) {
//BusinessFax
for($t = 0; $t <1;) {
//FirstName
for($y = 0; $y <1;) {
//LastName
for($u = 0; $u <1;) {
//Initials
for($a = 0; $a <1;) {
//Mobile
for($s = 0; $s <1;) {
//Notes
for($d = 0; $d <1;) {
//Phone
for($f = 0; $f <1;) {
//State
for($g = 0; $g <1;) {
//Postcode
for($h = 0; $h <1;) {
$result = array();
$contact = new \Picqer\Financials\Exact\Contact($connection);
$result = $contact->get();
foreach ($result as $contact) {
//Code
ob_start();
$i++;
echo $result[]= $contact->Code;
$filei[$i] = ob_get_contents();
ob_end_clean();
//echo $filei[$i]; //(Print available)
//AddressLine2
ob_start();
$o++;
echo $result[]= $contact->AddressLine2;
$fileo[$o] = ob_get_contents();
ob_end_clean();
//echo $fileo[$o]; //(Print available)
//BusinessPhone
ob_start();
$p++;
echo $result[]= $contact->BusinessPhone;
$filep[$p] = ob_get_contents();
ob_end_clean();
//echo $fileo[$p]; //(Print available)
//City
ob_start();
$q++;
echo $result[]= $contact->City;
$fileq[$q] = ob_get_contents();
ob_end_clean();
//echo $fileq[$q]; //(Print available)
//Country
ob_start();
$w++;
echo $result[]= $contact->Country;
$filew[$w] = ob_get_contents();
ob_end_clean();
//echo $filea[$a]; //(Print available
//Created
ob_start();
$e++;
echo $result[]= $contact->Created;
$filee[$e] = ob_get_contents();
ob_end_clean();
//echo $filee[$e]; //(Print available)
//Email
ob_start();
$r++;
echo $result[]= $contact->Email;
$filer[$r] = ob_get_contents();
ob_end_clean();
//echo $filer[$r]; //(Print available)
//BusinessFax
ob_start();
$t++;
echo $result[]= $contact->BusinessFax;
$filet[$t] = ob_get_contents();
ob_end_clean();
//echo $filet[$t]; //(Print available)
//FirstName
ob_start();
$y++;
echo $result[]= $contact->FirstName;
$filey[$y] = ob_get_contents();
ob_end_clean();
//echo $filey[$y]; //(Print available)
//LastName
ob_start();
$u++;
echo $result[]= $contact->LastName;
$fileu[$u] = ob_get_contents();
ob_end_clean();
//echo $fileu[$u]; //(Print available)
//Title
ob_start();
$a++;
echo $result[]= $contact->Title;
$filea[$a] = ob_get_contents();
ob_end_clean();
//echo $filea[$a]; //(Print available)
//Mobile
ob_start();
$s++;
echo $result[]= $contact->Mobile;
$files[$s] = ob_get_contents();
ob_end_clean();
//echo $files[$s]; //(Print available)
//Notes
ob_start();
$d++;
echo $result[]= $contact->Notes;
$filed[$d] = ob_get_contents();
ob_end_clean();
//echo $filed[$d]; //(Print available)
//Phone
ob_start();
$f++;
echo $result[]= $contact->Phone;
$filef[$f] = ob_get_contents();
ob_end_clean();
//echo $filef[$f]; //(Print available)
//State
ob_start();
$g++;
echo $result[]= $contact->State;
$fileg[$g] = ob_get_contents();
ob_end_clean();
//echo $fileg[$g]; //(Print available)
//Postcode
ob_start();
$h++;
echo $result[]= $contact->Postcode;
$fileh[$h] = ob_get_contents();
ob_end_clean();
//echo $fileh[$h]; //(Print available)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
//Witch contact 1,2,3,4,...
$k = 1;
//Code
echo 'Code = ' .$filei[1];
//AddressLine2
echo 'AddressLine = ' .$fileo[$k];
//BusinessPhone
echo 'BusinessPhone = ' .$filep[$k];
//City
echo 'City = ' .$fileq[$k];
//Country
echo 'Country = ' .$filew[$k];
//Created
echo 'created = ' . $filee[$k];
//Email
echo 'Email = ' . $filer[$k];
//BusinessFax
echo 'BusinessFax = ' . $filet[$k];
//FirstName
echo 'FirstName = ' .$filey[$k];
//LastName
echo 'Your last name = ' .$fileu[$k];
//Initials
echo 'Title = ' .$filea[$k];
//Mobile
echo 'Mobile = ' . $files[$k];
//Notes
echo 'Notes = ' . $filed[$k];
//Phone
echo 'Phone = ' . $filef[$k];
//State
echo 'State = ' . $fileg[$k];
//Postcode
echo 'Postcode = ' . $fileh[$k];
json_encode($result);
} catch (\Exception $e) {
json_encode(array(get_class($e) . ' : ' . $e->getMessage()));
}
=====================================
Autotask
Connection
=====================================
require_once 'C:\xampp\htdocs\Api\Autotask\vendor\autoload.php';
$username = '++++++++++++++++++++++++++++++++++++++';
$password = '++++++++++++++++';
$contact= new ATWS\AutotaskObjects\Contact();
$contact->id = 0; //0 for new and ID for update
$contact->AccountID = 236;
$contact->AlternatePhone = $filep[$k];
$contact->Country = $filew[$k];
$contact->CreatDate = $filee[$k];
$contact->EMailAddress = $filer[$k];
$contact->FirstName = $filey[$k];
$contact->LastName = $fileu[$k];
$contact->MobilePhone = $files[$k];
$contact->Phone = $filef[$k];
$contact->Active = '0';
//$contact->AddressLine = $fileo[$k];+
//$contact->City = $fileq[$k];+
//$contact->FaxNumber = $filet[$k];+
//$contact->MiddleInitial = $filea[$k];+
//$contact->Note = $filed[$k];+
//$contact->State = $fileg[$k];+
//$contact->ZipCode = $fileh[$k];+
echo "Auotask client created";
$authWsdl = 'https://webservices.autotask.net/atservices/1.5/atws.wsdl';
$opts = array('trace' => 1);
$client = new ATWS\Client($authWsdl, $opts);
$zoneInfo = $client->getZoneInfo($username);
$authOpts = array(
'login' => $username,
'password' => $password,
'trace' => 1, // Allows us to debug by getting the XML requests sent
);
$wsdl = str_replace('.asmx', '.wsdl', $zoneInfo->getZoneInfoResult->URL);
$client = new ATWS\Client($wsdl, $authOpts);
print_r($client->create($contact));
This is the output i get if i run php script.
Code = 24
AddressLine = 000000000
BusinessPhone = 040-44445511
City = 0000000
Country = NL
created = /Date(1493085780497)/
Email = test#sportmartbv.nl
BusinessFax = 000000
FirstName = Ellis
Your last name = Renners
Title = MEVR
Mobile = 0682121519
Notes = 000000000
Phone = 040-444151151
State = 000000000
Postcode = 00000000
I would like to get full user information in the output.
Any help would be greatly appreciated.
for($i=0;$i<1;){...} is equivalent to $i=0; while($i<1){...} so you should make sure that at some point, the value of $i changes and you leave the loop.
In your case, if $result = $contact->get(); returns an empty list, you won't enter the foreach and therefore you will never reach the parts with $x++ (replace x with whatever one-char variable you used in your nested for loops).
You usually add $i++ in the third input of a for, or you can add another condition, like :
$iterations=0;
for($i=0; $i<1 && $iterations<50; $iterations++)
{
...
}
so the loop will run until either $i >= 1 or $iterations >= 50.
Which means unless you change $iterations' value in your loop, it will run 50 times max.
This is in case you are expecting the input to change at some point, but if you really want to just get one input, you shouldn't use a for loop at all, instead just remove all those loops, remove all $x++; (with x in all your one-char variable names) and use only one variable initialized to 0.
Again, it is considered good practice to name your variables in an intelligible way.
Note : Nothing from the above is php-specific, this is algorithmics -- common to most programming languages.

Saving hex data in binary using PHP does not work properly

I am learning PHP and files and I am trying to write some code that put data in a binary file.
here's my code:
Write
<?php
echo "\n\nWRITE: \n\n";
$c = array();
$data = '';
$c['name'] = 'abcdefghijklmnopqrstuvwxyz';
$data .= implode('', $c);
$fp = fopen('test.bin', 'wb');
$len = strlen($data);
echo "\nFILE CONTENT: $data (strlen: $len)\n\n";
for ($i = 0; $i < $len; ++$i) {
$hx = dechex(ord($data{$i}));
fwrite($fp, pack("C", $hx));
}
echo "Last char is: $hx which mean: ";
echo chr(hexdec('7a'));
echo "\n--------------------------------------------\n";
fclose($fp);
Output
FILE CONTENT: abcdefghijklmnopqrstuvwxyz (strlen: 26)
Last char is: 7a which mean: z
Read
<?php
echo "\n--------------------------------------------\n";
echo "\n\nREAD: \n\n";
$fp = fopen('test.bin', 'rb');
$fseek = fseek($fp, 0, SEEK_SET);
if($fseek == -1) {
return FALSE;
}
$data = fread($fp, 26);
$arr = unpack("C*", $data);
$return = '';
foreach($arr as $val) {
$return .= chr(hexdec($val));
}
$n = '';
$arr = array();
$arr['name'] = substr($return, 0, 26);
print_r($arr);
echo "\n--------------------------------------------\n";
Output
Array
(
[name] => abcdefghipqrstuvwxy
)
Where are the missing letters like the z, m, n or o ?
EDIT 6-3-14 7h36 am: I would like to have the .bin file not plain text if possible
You are trying to set HEX chars in a char (C - unsigned char) instruction.
echo "\t";
foreach( array('0x41', 65, 'a') as $o )
echo $o."\t";
echo "\n";
foreach( array('c*','C*','a*','A*','h*','H*','v*','n*','S*') as $o ){
echo $o . "\t";
foreach( array(0x41, 65, "a") as $oo ) {
echo pack($o, $oo);
echo "\t";
}
echo "\n";
}
If you run this, you will see quickly how pack works with the 3 different values of a (HEX, DEC and normal).
You have to use the h instruction to accomplish what you need.
function writeToFile($data) {
$fp = fopen(FILENAME, 'wb');
$len = strlen($data);
for ($i = 0; $i < $len; ++$i) {
$hx = dechex(ord($data[$i]));
$result = fwrite($fp, pack("h*", $hx));
if(!$result) {
// show something
}
}
fclose($fp);
}
Now, for read that data. You will need to use the same one h and split the string you get back (split it using str_split with the parameter 2 since it's HEX 00 = 0 and FF = 255 - assuming you won't go over 255). Since h returns an array with a single element. Once you get your string back, you need to convert the number you get from the ord in the writeToFile using the chr function.
function readFromFile($lenght, $pos = 0) {
$return = '';
$fp = fopen(FILENAME, 'rb');
if(!$fp) {
// show something
}
$fseek = fseek($fp, $pos, SEEK_SET);
if($fseek == -1) {
// show something
}
$data = fread($fp, $lenght);
$data = unpack("h*", $data);
$arr = str_split(current($data), 2);
foreach($arr as $val) {
$return .= chr(hexdec($val));
}
return $return;
}
Now, you create your string and write to the file:
$data = 'This should work properly, thanks for StackOverFlow!';
$len = strlen($data);
writeToFile($data);
Then read back:
echo readFromFile($len);
The content of your file will look like this:
E<86><96>7^B7<86>öWÆF^Bwö'¶^B^G'ö^GV'Æ<97>Â^BG<86>^Væ¶7^Bfö'^B5G^V6¶ôgV'dÆöw^R

How to generate table of contents using dompdf?

I am using the dompdf library of php to generate PDF report from an HTML template. In that html template there is a section table of contents. When genrating PDF i need to update the page number of table of contents. Does anyone know how I can achieve this in dompdf library of php?
Thanks in advance.
I have achieved this in Drupal and guess it works on other php opensource and frameworks as well.
I have kept this code inside script tag
$GLOBALS['entity_page'][] = $pdf->get_page_number();
in template that stores the page number. Template is with extension tpl.php
Now in the module
after other codes for export I have added......
$canvas = $dompdf->get_canvas();
$font = Font_Metrics::get_font("helvetica", "normal");
$canvas->page_text(520, 805, "Page {PAGE_NUM}", $font, 9, array(0.4, 0.4, 0.4));
foreach ($GLOBALS['entity_page'] as $key => $val) {
$GLOBALS["entity_val"] = 0;
$GLOBALS["entity_y"] = 110;
$canvas->page_script('if($PAGE_NUM == 3 && $PAGE_NUM < 4){
$font = Font_Metrics::get_font("helvetica", "normal");
$x = 380;
$y = $GLOBALS["entity_y"];
$pdf->text($x, $y, "-------------------------".$GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 12, array(0, 0, 0, 0.8));
$GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 33;
$GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;
}');
}
$pdf->text this part adds the page numbers with constant increment in y axis position. Other global variables entity_y and entity_val are used to store the values.
Generating a Table of Contents from HTML (with h1,h2,h3), I did the following:
First give every header an unique ID (because we are using PrinceXML) and is a good pratice.
Then create an OL/LI structure of it, although that piece of code can contain bugs.
$matches = null;
$smatches = null;
$had_headers = array();
preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
if (!empty($matches[0]) && count($matches[0]) > 0)
foreach ($matches[0] as $headertag) {
preg_match('/>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
if (!empty($smatches[1]) && count($smatches[1]) > 0) {
$headerid = strip_tags($headertag);
$headerid = trim(strtolower(preg_replace('/[^a-z0-9]/i', '', $headerid)));
$smatches[2] = strtolower($smatches[2]);
$header_depth = intval(trim(str_ireplace('h', '', $smatches[2])));
while (in_array($headerid, $had_headers)) {
$headerid .= '1';
}
$had_headers[] = $headerid;
$content = str_replace($headertag, '<'. $smatches[2] . ' id="' . htmlentities($headerid) . '">' . $smatches[1] . '</' . $smatches[2] . '>', $content);
}
}
$matches = null;
$smatches = null;
$toc_html = '<ol id="toc">' . "\n";
$old_depth = 0;
$hadfirst = false;
preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
if (!empty($matches[0]) && count($matches[0]) > 0)
for ($i=0; $i < count($matches[0]); $i++) {
$headertag = $matches[0][$i];
preg_match('/<h[0-9][^>]*?id="(.*?)".*?>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
if (!empty($smatches[1]) && count($smatches[1]) > 0) {
$headerid = trim($smatches[1]);
$header_depth = intval(trim(str_ireplace('h', '', $smatches[3]))) - 1;
// don't take heigher than h3 in TOC
if ($header_depth > 2)
continue;
if ($header_depth < $old_depth) {
$diff = $old_depth - $header_depth; //if going multiple levels up
$toc_html .= '</li>'.str_repeat('</ol></li>', $diff);
} elseif ($header_depth > $old_depth) {
$toc_html .= '<ol>';
} else {
$toc_html .= ($hadfirst) ? '</li>' : null;
}
$toc_html .= '<li>' . htmlentities(trim(strip_tags($smatches[2]))) . '';
$old_depth = $header_depth;
$hadfirst = true;
}
}
$toc_html .= str_repeat('</li></ol>', ($old_depth + 1));
You may have solved this already? I've not used dompdf, but i did a similar thing in Zend_Pdf: I made a blank page for the table of contents and then went on creating all the other later pages, keeping an array of page_number => title. At the end I went back and updated the contents page using the reference saved earlier...
As an extension to vicky shrestha's answer here is what I have for a table of contents that expands more than a single page.
36 is just an arbitrary number of items that fit in the design.
foreach ($GLOBALS['entity_page'] as $key => $val) {
$GLOBALS["entity_y"] = 88;
$GLOBALS["entity_val"] = 0;
$GLOBALS["entity_per_page"] = 36;
if($val) {
$canvas->page_script('
if(isset($GLOBALS["entity_page"][$GLOBALS["entity_val"]])) {
if($PAGE_NUM == $GLOBALS["entity_page_number"]){
$x = 505;
$y = $GLOBALS["entity_y"];
$font = $fontMetrics->get_font("Open Sans", "Helvetica Neue", "Helvetica, Arial, sans-serif");
$pdf->text($x, $y, $GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 7, array(0, 0, 0, 1));
$GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 19;
$GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;
if (($GLOBALS["entity_val"] + 1) % $GLOBALS["entity_per_page"] == 0 ) {
$GLOBALS["entity_page_number"] = $GLOBALS["entity_page_number"] + 1;
$GLOBALS["entity_y"] = 31;
}
}
}');
}
}
the isset is important as for some reason the foreach will loop an additional time and you will get an out of bounds exception thrown at the end.
My idea to create TOC in pdf is following:
generate pdf where title of page would contain random token instead of name
parse generated pdf (using Smalot\PdfParser) and find page number which contains the token
generate the pdf where replace tokens with real name and found page number.
Example:
$html = "<html>
<div class='toc'>
<a>Article1 ..... {article_1_num}</a>
</div>
...
<div class='article'>
<div>{article_1_title}</div>
<div>Article content</div>
</div>
</html>";
//prepare variables to replace in template with random token
$vars = ["{article_1_title}"=>'676TGZGHVGFTRR655R66TTFTF', "{article_1_num}"=>0];
//genetate pdf
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
#$dompdf->render();
//create tamporary file
$temp = tempnam(sys_get_temp_dir(), 'prefix');
$output = $dompdf->output();
//save to temporary file
file_put_contents($temp, $output);
// parse pdf
$parser = new \Smalot\PdfParser\Parser();
$pdf = $parser->parseFile($temp);
$pages = $pdf->getPages();
$pageNum = 0;
//loop the pages and find the one with the token
foreach ($pages as $k=>$page) {
if(strpos($page,'676TGZGHVGFTRR655R66TTFTF') !== false){
$pageNum = $k+1;
break;
}
}
// remove temp file
unlink($temp);
//prepare variables with real values to replace in template
$vars = ["{article_1_title}"=>'Article no. 1', "{article_1_num}"=>$pageNum];
// generate pdf and stream it to user
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
#$dompdf->render();
$dompdf->stream("pdf.pdf", array('Attachment' => 0));

Categories