I need to configure one or more rows as header or footer to be printed on each page. I don't know the numbers of pages to print, it's dynamic.
Can I set several rows (or one) to be printed on each page using PHPExcel library?
From the PHPExcel documentation:
$objPHPExcel->getActiveSheet()
->getPageSetup()
->setRowsToRepeatAtTopByStartAndEnd(1, 5);
Or if you want to use Headers/Footers that aren't spreadsheet rows, but part of the page setup, then (again from the PHPExcel documentation).... useful for page numbering
$objPHPExcel->getActiveSheet()
->getHeaderFooter()
->setOddFooter('&L&B' . $objPHPExcel->getProperties()->getTitle() . '&RPage &P of &N');
Related
I've been struggling with this for a while and feel helpless.
Prestashop uses tcpdf to generate invoices and delivery slips from HTML templates filled using Smarty. We are working on updating the invoice design and found tcpdf to be lacking in CSS support. After some research we settled for wkhtmltopdf as the right tool from converting the HTML/CSS templates to PDF.
The problem
The store has a feature for exporting multiple invoices into a single PDF. Using TCPDF I was able to make the batch file ready for double sided printing by inserting a blank page after each invoice that had odd number of pages before the file was generated. But now that we switched to wkhtmltopdf I cannot achieve the same result.
The crucial problem is that while wkhtmltopdf allows for the usage of multiple HTML templates there seems to be no reliable way to determine the number of pages they are each going to have before the file is generated. The header and footer templates can receive the page count that the invoice ends up being but they are separate from the main content and therefore I cannot insert a page break accordingly.
I've also tried to calculate the height of the content / PDF page height but there were various issues with that once I started exporting multiple templates (worked alright with a single template). This approach isn't great either because inserting a blank page into the content itself causes the footer to appear on the new page as well which is not what I want.
My best attempt
The only way I've figured out that could get me around these issues is very inefficient. Each time a template is added to the batch I could pre-generate it using a separate instance of a wrapper for wkhtmltopdf, get the temporary file name, determine how many pages it has using pdfinfo and add a blank HTML template to the main instance accordingly. Here's a draft of a function to get the number of pages of the last template added (from a class that extends the wrapper, based on some other pdfinfo questions I found on SO):
/**
* Return the number of pages in the last invoice template added
* $complete === true => return the length of the entire document
*/
public function getNumPages($complete = false)
{
if (!$complete) {
// Generate PDF of last template added
$tmpPdf = new WKPdf($this->_options);
$tmpPdf->addPage($this->content, Array(
'footer-html' => $this->footer
));
/**
The createPdf method is protected so I need to get
the content as string here to force the wrapper to
call wkhtmltopdf.
*/
$tmpPdf->toString();
$document = $tmpPdf->getPdfFilename();
} else {
// Generate entire batch
$this->createPdf();
$document = $this->getPdfFilename();
}
// Use pdfinfo to get the PDF page count
$cmd = 'pdfinfo';
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;
}
This is very inefficient - it takes about 80 seconds to generate a batch of 25 invoices because I have to call wkhtmltopdf 25 times to create the temporary PDF files so that I can call pdfinfo 25 times to get their individual lengths and insert blank pages where necessary and then generate the final document.
The advantage of TCPDF is that it can give you the number of pages on the fly and a similar functionality takes about 5 seconds to generate a batch file of 25 invoices.
Anyone has any ideas on how to speed things up? Or a better idea to do this altogether. I've considered various tools for the generation including dompdf but wkhtmltopdf is simply the most powerful. The batch generation is really only used from the back office by the store admins so maybe they could be patient. But still.
Unfortunately wkhtmltopdf is the library, which is written in C language and we can not dynamically add one page on the fly like in PHP libraries.
Citate from your comment: Due to number of items ordered or ammount of customer data each invoice can be anywhere from 1 to 3 pages long.
And because of this we can not precalculate the number of pages and write it to a database.
I think you have only one possibility / solution: you have to write behind each invoice a blank page and after the whole PDF was generated you have to edit it with free PHP library like FPDI. In combination with FPDI it is even possible to edit PDF documents.
By PDF editing you could delete all blank pages which you do not need if they starts with odd page number (like 3, 5, etc.). And in FPDI you have the possibility to detect a page number. It is much faster than the solution which you use now.
And the blank(or empty) pages you could detect on content length with FPFI like follows:
<?php
require('fpdf.php');
require_once('setasign/Fpdi/autoload.php');
class Pdf extends \setasign\Fpdi\Fpdi
{
private $pdfReader;
function Header()
{
if(is_null($this->pdfReader))
{
$readerId = $this->getPdfReaderId('blank-pages.pdf');
$this->pdfReader = $this->getPdfReader($readerId);
}
$page_fpdi = $this->pdfReader->getPage($this->PageNo());
$this->content = $page_fpdi->getContentStream();
$this->Cell(0, 15, 'page content length: '.strlen($this->content));
}
protected function _putimages(){}
}
$pdf = new Pdf();
$pdf->SetFont('Arial', '', 12);
$pdf->AddPage(); //page content length: 70 // page with 'Hello World!' string
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->AddPage(); //page content length: 30 // empty page
$pdf->Output();
?>
My blank-pages.pdf I have generated using FPDF with following code:
<?php
require('fpdf.php');
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$pdf->AddPage();
$pdf->AddPage();
$pdf->Output();
?>
Is there a way, after calling $mpdf->WriteHTML( $html ) to either call "undo" or reset it back to a blank state?
I've looked here and found nothing so far.
You can use the DeletePages() function to delete / reset the content
I'm using 1.7.5 version of Mpdf and now I am confused and devastated. I was trying to use same Mpdf instance for multiple documents (config dependant). I was trying to make it this way:
// for each document
$this->mpdf->WriteHtml($content);
$this->mpdf->Output($path, Destination::FILE);
// below does pretty much nothing O.o
$this->mpdf->DeletePages(0, $this->mpdf->PagesNo());
$this->mpdf->Reset();
Without DeletePages() on second document content of PDF file was bizarre. Text was from first file, title from second, file size suggested two files merged and content was sum of pages, where after content of first document pages were blank (not empty as white, but blank, as transparent). The only difference after using DeletePages() was that content was only first document with title from second (in metadata).
For me, the only solution was not to inject Mpdf into my Service, but 'generator': function without arguments that hid initiation of Mpdf and on call provided new 'fresh' Mpdf instance.
I have a HTML document which I'm converting to PDF using TCPDF. The HTML document contains several html tables. This works fine. But, I also need to add the number of pages in the PDF doc to the first page. Is there any solution to build the PDF file and then add a page before current first page ?
I don't know if it works with HTML, but I found a solution that worked for me with movePage.
You create the page, get its position and move it to the first position :
$pdf->AddPage();
$pdf->Cell(0, 10, 'My content');
// Get the current page number
$pageNo = $pdf->PageNo();
// First page of the document
$toPage = 1;
$pdf->movePage($pageNo, $toPage);
Official example here : https://tcpdf.org/examples/example_044/
You should be able to calculate how many pages there will be based on the size of your tables. I would read in all the html data, measure it, and then start to make the PDF.
The idea I found is to clone the TCPDF instance and get the returned number of pages:
$tmp_pdf = clone $pdf;
$tmp_pdf->AddPage();
$tmp_pdf->writeHTML($report, true, false, true, false, '');
$num_pages = $tmp_pdf->getNumPages();
unset($tmp_pdf);
$report = preg_replace('#\{num_pages\}#is', $num_pages, $report);
So I am exporting an Excel file I created with PHPExcel to PDF. My only problem is when the content runs over the width of the page (not the length) that content disappears. Only a blank page is created where that content should be. I've tried inserting a column break with a line like this example from the documentation, but only row breaks seem to work with PDF:
$objPHPExcel->getActiveSheet()->setBreak( 'D10' , \PHPExcel_Worksheet::BREAK_COLUMN );
It's worth mentioning I'm working from within a Symfony2 controller, hence the slash in front of the PHPExcel enum.
My ideal solution is to have any extra content run over to a second page that can be placed alongside the first page to show the entire table as it appears in a real Excel document.
This is a limitation of the external library used by PHPExcel to render html output as PDF (namely tcPDF).
While I'm aware of a number of other libraries that can generate PDF from HTML (such as domPdf and mPdf) and future versions of PHPExcel will allow you to select which of these libraries you wish to use, none of them seem capable of generating multiple pages to show data from the full width of a worksheet.
Unless anybody can suggest alternatives, I'm afraid that this will remain a limitation for the foreseeable future.
I was having a similar problem vertically,
the solution i found was to use this
$highestRow = $objPHPExcel->setActiveSheetIndex(0)
->getHighestRow();
$objPHPExcel->setActiveSheetIndex(0)
->getPageSetup()
->setPrintArea("A1:I" . $highestRow )
->setFitToHeight(255);
i would suggest trying something similar with width.
for example :
$highestColumn= $objPHPExcel->setActiveSheetIndex(0)
->getHighestColumn();
$objPHPExcel->setActiveSheetIndex(0)
->getPageSetup()
->setPrintArea("A1:" . $highestColumn . "10" )
->setFitToWidth(255);
I've developed an application that users can use to store lists of their favourite books, books they've read, books they'd like to read, etc., and it's all working fine, but it's just suddenly struck me that users may want to print off their list or download their list to use in another format such as a CSV file to import into a spreadsheet.
Obviously users could print the entire web page, but I'd like to give them the ability to just print their list, or to download it as say a CSV file for use in a spreadsheet.
What would be the easiest way to do this, apart from passing the query result to a HTML page and printing the HTML page?
For printing, most users appreciate the simplicity of a "cleaned-up" html page showing only/mostly the data they are interested in. In this fashion they can stay within the web browser to get a quick print of what they need. As indicated by Tom, this can often be achieved with the CSS whereby the undesired elements in a printout can be hidden and other elements may be shown with a slightly different style (for example to introduce more constrast, assuming a B/W printer etc.).
For exporting the data, you'll need to introduce a link / button which the users can click to signify their desire to get the data exported [to a particular format]. Your application can then serve this under the proper MIME type (i.e. you'll modify the http header 's Content-type (to be something like "Application/text") before streaming the CSV text)
To output a csv you can simply format the output from your query as a csv file, then set the content-disposition header for teh output. This snippet is taken from this website.
<?
$csv_output = "column 1,column2";
$csv_output .= "\n";
$result = mysql_query("select * from table");
while($row = mysql_fetch_array($result)) {
$csv_output .= "$row[col1],$row[col2]\n";
}
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: csv" . date("Y-m-d") . ".xls");
print $csv_output;
exit;
?>
Obviously users could print the entire web page, but I'd like to give them the ability to just print their list, or to download it as say a CSV file for use in a spreadsheet.
Why not provide a print stylesheet, simple things like hiding the nav and menu and increase the font size.