TCPDI useTemplate excedes timeout with somes pdf - php

I'm triying to put a text into some pdfs with TCPDI.
It works fine in most pdfs, but in some pdf the code get stuck when it reachs the useTemplate() function, and got 500 error (Maximum time exceded).
They are not long pdf (1,2,3 pages max), and anothers pdfs with more pages works fine. Here is my code:
$pdf = new TCPDI();
$pageCount = $pdf->setSourceFile($path);
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
$templateId = $pdf->importPage($pageNo);
$size = $pdf->getTemplateSize($templateId);
if ($size['w'] > $size['h']) {
$pdf->AddPage('L', array($size['w'], $size['h']));
} else {
$pdf->AddPage('P', array($size['w'], $size['h']));
}
$pdf->useTemplate($templateId); //Here is where it takes so long that it exceeds time
$pdf->SetFont('Helvetica');
$pdf->SetFontSize(10);
$pdf->SetTextColor(255, 0, 0);
$pdf->SetXY(2, 0);
$pdf->Write(0, 'Code nÂș 4');
}
$pdf->Output($file,'D');
Is there any prop of the pdf that can make it stay locked? There are alterntives?
I have a code that process several pdf with this code in a loop an put they into a zip, and when in the chain is one pdf that jam the code the zip obviously dind't process, so if there are a way to detect what pdf are goingo to give me problem, I cant jump over there and generate the zip with the goods one.
I have not control over the pdfs, their are upload by a lot of clients
EDIT: In log there are more than a million of lines similar to PHP Warning: Illegal string offset 'DAmip' in ...\TCPDF\tcpdi_parser.php on line 712 before Maximum execution Time Fatal Error

Same problem for me.
I 'solved' the situation adding the line set_time_limit(10); before the line
while (strspn($data{$offset}, "\x00\x09\x0a\x0c\x0d\x20") == 1) {
I know it's not the right solution but, at least, doesn't block your program

I suggest you to read my comment on GitHub : https://github.com/pauln/tcpdi_parser/pull/23
The function getDictValue() of tcpdi_parser.pdf is incompatible with some PDF files.
All is explained in my response dated of this day.
Suggested modification :
private function getDictValue($offset, &$data) {
$objval = array();
// Extract dict from data
$i = 1;
$dict = "";
$offset+= 2;
$is_bracket = false;
do {
if ($data[$offset] == "[") {
$is_bracket = true;
$dict.= $data[$offset];
} else if ($data[$offset] == "]") {
$is_bracket = false;
$dict.= $data[$offset];
} else if (!$is_bracket && ($data[$offset] == "<") && ($data[$offset + 1] == "<")) {
$i++;
$dict.= "<<";
$offset++;
} else if (!$is_bracket && ($data[$offset] == ">") && ($data[$offset + 1] == ">")) {
$i--;
$dict.= ">>";
$offset++;
} else {
$dict.= $data[$offset];
}
$offset++;
} while ($i > 0);
// Now that we have just the dict, parse it.
$dictoffset = 0;
do {
// Get dict element.
file_put_contents("debug_tcpdi.txt", "getRawObject($dictoffset, data) depuis getDictValue()" . "\r\n", FILE_APPEND);
list($key, $eloffset) = $this->getRawObject($dictoffset, $dict);
if ($key[0] == '>>') {
break;
}
file_put_contents("debug_tcpdi.txt", "getRawObject($eloffset, data) depuis getDictValue()" . "\r\n", FILE_APPEND);
list($element, $dictoffset) = $this->getRawObject($eloffset, $dict);
$objval['/'.$key[1]] = $element;
unset($key);
unset($element);
} while (true);
return array($objval, $offset);
}

Related

How to create a log file in PHP

I have 2 functions to parse each one a file Excel their sizes are 673K and 131K. They have the same code just their names.
One function it read the data from file Excel and the other function it return:
Fatal Error: Allowed Memory Size of 134217728 Bytes Exhausted (Tried to allow 72 bytes)
I have others files Excel their sizes more bigger than this ones and their parsing functions works well.
I want to create a logfile to register every action they do inside the system but I have no idea how to do it. On the other hand I found this solution in Stackoverflow for #Lawrence Cherone:
enter link description here
But the problem is the first time I will do a logfile, I don't know how I create it ? I create a new file and I put it in my project ? How I excute it and how I can see the reason of the error in my function ? Or I put this file in the function when I have the error like the solution proposed by #Lawrence Cherone ?
This solution it seems worked fine and the problem is resolved.
This following is my small code of my function, can you guide me how I create this logfile to debug it:
public function parseEquipment($filePath = null) {
set_time_limit(0);
$listEquipement = [];
$count = 0;
$chunkSize = 1024;
$objReader = PHPExcel_IOFactory::createReader(PHPExcel_IOFactory::identify($filePath));
$spreadsheetInfo = $objReader->listWorksheetInfo($filePath);
$chunkFilter = new \Floose\Parse\ChunkReadFilter();
$objReader->setReadFilter($chunkFilter);
$objReader->setReadDataOnly(true);
$chunkFilter->setRows(0, 1);
$objPHPExcel = $objReader->load($filePath);
$totalRows = $spreadsheetInfo[0]['totalRows'];
for ($startRow = 1; $startRow <= $totalRows; $startRow += $chunkSize) {
$chunkFilter->setRows($startRow, $chunkSize);
$objPHPExcel = $objReader->load($filePath);
$sheetData = $objPHPExcel->getActiveSheet()->toArray(null, null, true, false);
$startIndex = ($startRow == 1) ? $startRow : $startRow - 1;
if (!empty($sheetData) && $startRow < $totalRows) {
$dataToAnalyse = array_slice($sheetData, $startIndex, $chunkSize);
//echo 'test1';
if($dataToAnalyse[1][0]==NULL){
//echo 'test2';
break;
}
//echo 'test3';
//var_dump($sheetData);
for ($i = 0; $i < $chunkSize; $i++) {
if ($dataToAnalyse[$i]['0'] != NULL) {
//echo 'OK';
$listEquipement[] = new Article($dataToAnalyse[$i]['3'], $dataToAnalyse[$i]['4'], $dataToAnalyse[$i]['2']);
// echo 'test4';
$count++;
}
}
}
$objPHPExcel->disconnectWorksheets();
unset($objPHPExcel, $sheetData);
}
//var_dump(array_slice($sheetData, $startIndex, $chunkSize););
return $listEquipement;
}
error_log("You messed up!".$my_message, 3, "/var/tmp/custom-errors.log");

When using Magento, PDF cuts off after first page

Using Magento with Zend_Pdf and a couple of custom classes that allow for the printing/downloading of PDFs with certain header/table specifications. The problem is that it cuts off after the first page and doesn't create a new page when the items list is more than about 20 items.
This code:
public function getOutput()
{
$this->pages[] = $this->page;
return $this->render();
}
And this code:
$pdf = new PrintPdf();
$pdf->generate($sourceData);
$output = $pdf->getOutput();
echo $output;
Are where I believe the error is happening in. If I change the "return" in the first code to "echo" it will output the correct data to the browser, but when I then try and pass it through the second code, it only puts out one page of data.
Any advice would be appreciated as I have been working on this for about 2 weeks.
UPDATE:
I was told this piece of code:
private function drawLineItems($tableData)
{
$this->drawHeading($this->__('Line Items'));
// Draw table
$this->decOffset(35);
$this->colorLine(cBLUE);
$this->page->setLineWidth(0.5);
$this->page->drawLine($this->pMargin, $this->yOffset, $this->pWidth - $this->pMargin, $this->yOffset);
$this->fontSize(FONT_SMALL);
$this->colorFill(cBLUE);
$this->decOffset(15);
$sum = ($this->pMargin + 10);
for($idx = 0; $idx < sizeof($tableData['heading']); $idx++) {
$pos = $sum;
$this->page->drawText($tableData['heading'][$idx], $sum, $this->yOffset);
$sum += ($tableData['width'][$idx] + 10);
$tableData['width'][$idx] = $pos;
}
$this->decOffset(10);
$this->page->drawLine($this->pMargin, $this->yOffset, $this->pWidth - $this->pMargin, $this->yOffset);
$this->fontSize(8);
$this->colorFill(cLIGHT);
$this->colorLine(cBORDER);
foreach($tableData['rows'] as $row) {
$this->decOffset(15);
$yOffset = $this->yOffset;
for($idx = 0; $idx < sizeof($row); $idx++) {
if ($tableData['heading'][$idx] == 'Description') {
$lines = $this->_breakTextToLines($row[$idx], $tableData['width'][$idx + 1] - $tableData['width'][$idx]);
foreach ($lines as $line) {
$this->page->drawText($line, $tableData['width'][$idx], $yOffset);
$yOffset -= 10;
}
} else {
$this->page->drawText($row[$idx], $tableData['width'][$idx], $this->yOffset);
}
}
$this->decOffset($this->yOffset - $yOffset);
$this->page->drawLine($this->pMargin, $this->yOffset, $this->pWidth - $this->pMargin, $this->yOffset);
}
$this->decOffset(20);
$this->fontSize(FONT_NORMAL);
$this->colorFill(cDARK);
$this->page->drawText($this->__('Grand Total') . ': ' . $tableData['total'], $this->pWidth - 125, $this->yOffset);
}
is probably where the problem is as it doesn't form a new page after a certain number of items. My problem now lies in accomplishing this task. Help would still be appreciated.
I found that adding this code:
if( $this->yOffset < 25){
$this->yOffset = $this->pHeight - 25;
$yOffset = $this->yOffset;
$this->pages[] = $this->page;
$this->page = $this->newPage(Zend_Pdf_Page::SIZE_A4);
$this->fontSize(8);
$this->colorFill(cLIGHT);
$this->colorLine(cBORDER);
}
after
$yOffest = $this->yOffset
in the third part of the code in the question, fixed the problem!

Getting total number of page using pdfinfo not showing result

I am using pdfinfo for getting total number of pages in a pdf file using php.
function getPDFNoOfPages($document) {
$cmd = "C:/wamp/www/PhpProject1/pdfinfo.exe";
exec("$cmd $document", $output);
$pagecount = 0;
foreach ($output as $op) {
// Extract the number
if (preg_match("/Pages:\s*(\d+)/i", $op, $matches) === 1) {
$pagecount = intval($matches[1]);
break;
}
}
return $pagecount;
}
$document = 'test.pdf';
echo getPDFNoOfPages($document);
here my pdf file is in
C:/wamp/www/PhpProject1/test.pdf
but it showing output '0' .how to solve this.thanks in advance

Why does this code cause high load average on the server?

The code below sends a msg when a value, obtained from a not so stable API, is within a certain range.
This code causes the load average, but not the CPU usage, to go up, likely due to high I/O wait.
CentOS 6.5
Kernel 2.6.32-431.11.2.el6.x86_64
Apache 2.2.15
PHP 5.3.3 (mod_fcgid/2.3.7)
> cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]
The code had the same problematic effect on both dedicated hardware and on the cloud (both cases as a VM on KVM/Virtio).
What could be done to keep this code from *causing the processor to wait on instruction completion before processing new instructions**? I understand that lowering the timeout doesn't really solve the problem, only diminishes its impact.
*this is my understanding why this code causes load average to go up.
<?php
$min = '1';
$last_min = '1';
if (!empty($_GET['min'])) {
$min = $_GET['min'];
} else {
$min = false;
}
$ctx=stream_context_create(array('http'=>
array(
'timeout' => 10 // seconds timeout
)
));
$json = file_get_contents('https://www.domain.com/api/ticker/',false,$ctx);
if (!empty($json )) {
echo($json);
if (#file_get_contents('log.txt')) {
if (quote_changed($json)) {
file_put_contents('log.txt', $json, FILE_APPEND);
}
} else {
file_put_contents('log.txt', $json);
}
$obj = json_decode($json,true);
$last = $obj['ticker']['last'];
if (is_numeric($last)) {
$last = (int)$last;
$last_min = #file_get_contents('last_min.txt');
$notified = #file_get_contents('notified.txt');
if ($notified === false) {
$notified = 'false';
#echo "no notify file\n";
}
if (($last_min === false) || (($min) && ($last_min <> $min))) {
$last_min = 1;
$notified = 'false';
file_put_contents('last_min.txt', $min);
#echo "no min file or diff min\n";
}
#echo ('notified='.$notified.'\n');
if (($last >= $min) && ($notified=='false')) {
#$url = ('http://otherdomain.com/nexmo/sendmsg.php' . '?name=blah' . $last);
#file_get_contents($url);
#switch to SMS when going abroad and plugin new number when available
mail("8885551212#mail.net","Blah at".$last,"","From: gaia#domain.com\n");
file_put_contents('notified.txt', 'true');
#echo "msg sent\n";
} elseif (($last < $min) && ($notified=='true')) {
file_put_contents('notified.txt', 'false');
#echo "not sent\n";
}
}
}
function quote_changed($current) {
$previous = tailCustom('log.txt');
#echo ('previous='.$previous);
if ($previous === (trim($current))) {
return 0;
} else {
return 1;
}
}
function tailCustom($filepath, $lines = 1, $adaptive = true) {
// Open file
$f = #fopen($filepath, "rb");
if ($f === false) return false;
// Sets buffer size
if (!$adaptive) $buffer = 4096;
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
// Jump to last character
fseek($f, -1, SEEK_END);
// Read it and adjust line number if necessary
// (Otherwise the result would be wrong if file doesn't end with a blank line)
if (fread($f, 1) != "\n") $lines -= 1;
// Start reading
$output = '';
$chunk = '';
// While we would like more
while (ftell($f) > 0 && $lines >= 0) {
// Figure out how far back we should jump
$seek = min(ftell($f), $buffer);
// Do the jump (backwards, relative to where we are)
fseek($f, -$seek, SEEK_CUR);
// Read a chunk and prepend it to our output
$output = ($chunk = fread($f, $seek)) . $output;
// Jump back to where we started reading
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
// Decrease our line counter
$lines -= substr_count($chunk, "\n");
}
// While we have too many lines
// (Because of buffer size we might have read too many)
while ($lines++ < 0) {
// Find first newline and remove all text before that
$output = substr($output, strpos($output, "\n") + 1);
}
// Close file and return
fclose($f);
return trim($output);
}
?>
Move the data into a database or if that's not an option, cache the unstable file into your server locally so you don't have to hit the external provider every time.
If neither of those are an option, it seems to me that the people providing the API need to improve their performance, you can verify this by just benchmarking how many milliseconds each hit takes through Firebug.

PHP: Read from certain point in file

Similar to: How to read only 5 last line of the text file in PHP?
I have a large log file and I want to be able to show 100 lines from position X in the file.
I need to use fseek rather than file() because the log file is too large.
I have a similar function but it will only read from the end of the file. How can it be modified so that a start position can be specified as well? I would also need to start at the end of the file.
function read_line($filename, $lines, $revers = false)
{
$offset = -1;
$i = 0;
$fp = #fopen($filename, "r");
while( $lines && fseek($fp, $offset, SEEK_END) >= 0 ) {
$c = fgetc($fp);
if($c == "\n" || $c == "\r"){
$lines--;
if($revers){
$read[$i] = strrev($read[$i]);
$i++;
}
}
if($revers) $read[$i] .= $c;
else $read .= $c;
$offset--;
}
fclose ($fp);
if($revers){
if($read[$i] == "\n" || $read[$i] == "\r")
array_pop($read);
else $read[$i] = strrev($read[$i]);
return implode('',$read);
}
return strrev(rtrim($read,"\n\r"));
}
What I'm trying to do is create a web based log viewer that will start from the end of the file and display 100 lines, and when pressing the "Next" button, the next 100 lines preceding it will be shown.
If you're on Unix, you can utilize the sed tool. For example: to get line 10-20 from a file:
sed -n 10,20p errors.log
And you can do this in your script:
<?php
$page = 1;
$limit = 100;
$off = ($page * $limit) - ($limit - 1);
exec("sed -n $off,".($limit+$off-1)."p errors.log", $out);
print_r($out);
The lines are available in $out array.
This uses fseek to read 100 lines of a file starting from a specified offset. If the offset is greater than the number of lines in the log, the first 100 lines are read.
In your application, you could pass the current offset through the query string for prev and next and base the next offset on that. You could also store and pass the current file position for more efficiency.
<?php
$GLOBALS["interval"] = 100;
read_log();
function read_log()
{
$fp = fopen("log", "r");
$offset = determine_offset();
$interval = $GLOBALS["interval"];
if (seek_to_offset($fp, $offset) != -1)
{
show_next_button($offset, $interval);
}
$lines = array();
for ($ii = 0; $ii < $interval; $ii++)
{
$lines[] = trim(fgets($fp));
}
echo "<pre>";
print_r(array_reverse($lines));
}
// Get the offset from the query string or default to the interval
function determine_offset()
{
$interval = $GLOBALS["interval"];
if (isset($_GET["offset"]))
{
return intval($_GET["offset"]) + $interval;
}
return $interval;
}
function show_next_button($offset, $interval)
{
$next_offset = $offset + $interval;
echo "Next";
}
// Seek to the end of the file, then seek backward $offset lines
function seek_to_offset($fp, $offset)
{
fseek($fp, 0, SEEK_END);
for ($ii = 0; $ii < $offset; $ii++)
{
if (seek_to_previous_line($fp) == -1)
{
rewind($fp);
return -1;
}
}
}
// Seek backward by char until line break
function seek_to_previous_line($fp)
{
fseek($fp, -2, SEEK_CUR);
while (fgetc($fp) != "\n")
{
if (fseek($fp, -2, SEEK_CUR) == -1)
{
return -1;
}
}
}
Is "position X" measured in lines or bytes? If lines, you can easily use SplFileObject to seek to a certain line and then read 100 lines:
$file = new SplFileObject('log.txt');
$file->seek(199); // go to line 200
for($i = 0; $i < 100 and $file->valid(); $i++, $file->next())
{
echo $file->current();
}
If position X is measured in bytes, isn't it a simple matter of changing your initial $offset = -1 to a different value?
I would do it as followed:
function readFileFunc($tempFile){
if(#!file_exists($tempFile)){
return FALSE;
}else{
return file($tempFile);
}
}
$textArray = readFileFunc('./data/yourTextfile.txt');
$slicePos = count($textArray)-101;
if($slicePos < 0){
$slicePos = 0;
}
$last100 = array_slice($textArray, $slicePos);
$last100 = implode('<br />', $last100);
echo $last100;

Categories