This topic is locked

Create complex PDF documents from templates

9/13/2019 11:15:48 AM
PHPRunner Tips and Tricks
fhumanes author


In other articles I have explained how we can generate an invoice (example of dynamic document chosen for the example) in Word format and / or Excel format.
Also, then I exposed and showed how we can do a webservices to convert any MS Office document to PDF (I am very proud of this solution).
The webservices is dependent on S.O. Windows and the process has a response time that is not immediate at all.
So, many people and myself, we had as pending look for a solution that would allow us to create quality documents, directly in PDF, quickly and easily.
To make it quick and easy, I have decided that we should start with a PDF template, which would greatly simplify having documents with very good quality and that we should not have to start with a "blank sheet", because that would mean that the PHP programmer would be designing PDF documents in a slow and very artisanal way.
On the Internet there are many libraries for PHP to create and modify PDF files, but in this case I have selected the Setasin solution https://www.setasign.com/products/fpdi/about/ because there is a company currently active and It perfectly matched the functionality that I wanted to incorporate into the example.


To make the PDF template I used Excel (it can be done with almost any software but Excel is one of the best solutions).
I started from the template I had for the solution to create excel, but I had to define multiple invoice lines, since this solution does not allow dynamic template objects (this is one of the limitations of using PDF templates, but it is which most closely resembles the classic solution of paper forms.


In the use of the library, we must specify the X and Y coordinates (in points) where we want to enter the texts.

Almost all viewers / reader of PDF files have the option to measure. I have used the Foxit Reader solution https://www.foxitsoftware.com/pdf-reader/, but everyone should use what they find most comfortable.


The product used tells me that each page has the size of 595.32 X 841.92 (dots).
When we use the measuring tool it indicates the distance in X axis or Y axis from one point to another. This is very important since to position the data in the template we will have to indicate the X and Y coordinates from the point 0,0.


This is an image of an example output.
Things we have to keep in mind:

<?php

require_once __DIR__ . '/../../ComponentCode/fpdi_2_2/autoload.php';
use setasign\Fpdi\Fpdi;
// initiate FPDI

$pdf = new Fpdi();

// add a page

$pdf->AddPage();

// set the source file

$template_pdf = __DIR__.'/PlantillaFactura.pdf';

$pdf->setSourceFile($template_pdf);

// import page 1

$tplIdx = $pdf->importPage(1);

// use the imported page and place it at position 10,10 with a width of 100 mm

// $pdf->useTemplate($tplIdx, 10, 10, 100);

$pdf->useTemplate($tplIdx);
// Obtain measures from the page for the transformation of the Points

$pdf->SetXY(1, 1);

$wPt = 595.32; // Measures in points of the page

$w = $pdf->GetPageWidth();

$hPt = 841.92; // Measures in points of the page

$h = $pdf->GetPageHeight();

$coef_x = $wPt/$w; // X axis transformation coefficient

$coef_y = $hPt/$h; // Y axis transformation coefficient
// now write some text above the imported page

// $pdf->SetFont('Arial','B',10); // Font, type and size

// $pdf->SetTextColor(68, 68, 68); // Color in R, G, B

// $pdf->SetXY(132/$coef_x, 118/$coef_y); // Positioning on the page

// $pdf->Write(0, 'This is just a simple text'); // Write TXT. Offset in Y points, Text and Link
// -----------------------------------------------Recover invoice data-------------------------------------------------------------------------------------------------------

$idfactura= $_SESSION['idfactura'] ; // invoice identification to obtain
$sql="SELECT Nif, NombreRazonSocial, Domicilio, RestoDomicilio, FechaFactura, TotalFactura FROM factura where idfactura = $idfactura";

$resql=db_query($sql,$conn);

$data=db_fetch_array($resql);

// Variables on different parts of document

$pdf->SetFont('Arial','',16); // Font, type and size

$pdf->SetTextColor(247, 172, 8); // Color in R, G, B

$pdf->SetXY(321/$coef_x, 101/$coef_y); // Positioning on the page

$pdf->Write(0, '#'.$idfactura); // Numbre invoce

$pdf->SetXY(435/$coef_x, 101/$coef_y); // Positioning on the page

$pdf->Write(0, $data['FechaFactura']); // Date invoce
$pdf->SetFont('Arial','B',10); // Font, type and size

$pdf->SetTextColor(68, 68, 68); // Color in R, G, B

$pdf->SetXY(132/$coef_x, 118/$coef_y); // Positioning on the page

$pdf->Write(0, $data['Nif']); // NIF
$pdf->SetXY(132/$coef_x, 132/$coef_y);
$string = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $data['NombreRazonSocial']); // Convert UTf8

$pdf->Write(0, $string); // Nombre
$pdf->SetFont('Arial','',10); // Font, type and size

$pdf->SetXY(132/$coef_x, 146/$coef_y);

$string = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $data['Domicilio']); // Convert UTf8

$pdf->Write(0, $string); // Domicilio



$pdf->SetXY(132/$coef_x, 160/$coef_y);

$string = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $data['RestoDomicilio']); // Convert UTf8

$pdf->Write(0, $string); // RestoDomicilio
$pdf->SetFont('Courier','',10); // Font, type and size, COURIER is a fixed size font of all characters

$pdf->SetXY(443/$coef_x, 481/$coef_y);
$number = number_format($data['TotalFactura'], 2, ',', '.');

$number = sprintf("%' 11s",$number);

$pdf->Write(0,$number ); // TotalFactura

$pdf->SetXY(443/$coef_x, 453/$coef_y);

$pdf->Write(0,$number ); // TotalFactura
// --------------------------------------------------------------

$sql="SELECT producto_idproducto, Nombre, Precio, Cantidad, Valor FROM linea_factura where factura_idfactura= $idfactura ";

$rsSql=db_query($sql,$conn);

$countLines=0;

while ($data2 = db_fetch_array($rsSql)){
$pdf->SetXY(55/$coef_x, ((206.5+($countLines*14.63))/$coef_y));

$number = number_format($data2['producto_idproducto'], 0, ',', '.');

$number = sprintf("%' 9s",$number);

$pdf->Write(0,$number ); // Id Producto
$pdf->SetXY(128/$coef_x, ((206.5+($countLines*14.63))/$coef_y));

$string = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $data2['Nombre']); // Convert UTf8

$pdf->Write(0,$string); // Nonbre
$pdf->SetXY(322.71/$coef_x, ((206.5+($countLines*14.63))/$coef_y));

$number = number_format($data2['Precio'], 2, ',', '.');

$number = sprintf("%' 7s",$number);

$pdf->Write(0,$number ); // Precio
$pdf->SetXY(375/$coef_x, ((206.5+($countLines*14.63))/$coef_y));

$number = number_format($data2['Cantidad'], 0, ',', '.');

$number = sprintf("%' 10s",$number);

$pdf->Write(0,$number ); // Cantidad
$pdf->SetXY(443/$coef_x, ((206.5+($countLines*14.63))/$coef_y));

$number = number_format($data2['Valor'], 2, ',', '.');

$number = sprintf("%' 11s",$number);

$pdf->Write(0,$number ); // Valor
$countLines=$countLines+1;
}
// adding the second page of the template

$tplIdx2 = $pdf->importPage(2);

$s = $pdf->getTemplatesize($tplIdx2);

$pdf->AddPage('', $s);

$pdf->useImportedPage($tplIdx2);
$pdf->Output('I','Factura.pdf');

/*

// -------------------- foot to save the new PDF document ------------------

$temp_file = tempnam(sys_get_temp_dir(), 'PDF');

$pdf->Output('F',$temp_file);
// ------------------ Operation with file result -------------------------------------------

$documento = file_get_contents($temp_file);

unlink($temp_file); // delete file tmp

header("Content-Disposition: attachment; filename= factura.pdf");

header('Content-Type: application/pdf');

echo $documento;

*/
?>


All these PHP libraries are quite heavy and so that the PHPRunner projects are not heavy and also allow me to share these libraries for several projects, I always create a "ComponentCode" directory, at the same deployment height as the projects, where I leave The bookstores. In this example are the libraries of:
Fpdi

PHPWord

PhpSpreadsheet
Keep this in mind if you download the example, because you will have to download these libraries.
You can see the result in this DEMO link https://fhumanes.com/factura/
As usual in these articles, I leave the files so you can test it on your computers.
For any questions or what you need, you indicate it to me through my email fernandohumanes@gmail.com.
To download the example, access https://fhumanes.com/blog/otros-ejemples/crear-factura-con-setasign-pdf/

lefty 9/13/2019

Great Article. Although new PDF generation is coming in 10.3 from what I have heard.

fhumanes author 9/16/2019



Great Article. Although new PDF generation is coming in 10.3 from what I have heard.



Thank you. The solution that has and arrives in the new versions start from the proposal to create HTML and pass this to PDF. I think the solutions are very different (neither better nor worse) and that both are excellent solutions to consider if you want to obtain PDF documents directly.

Regards,

A
acpan 10/2/2019

Hi fhumanes,
Tried it. I am totally impressed, thank you so much for taking time to translate to English and post here to share.
One thing, why do you not use the PHPWord PDF convert function? Because, from your demo, we can edit the word template easily to create template, i tried this, using this we don't need to measure the x and y coordinates, just position the "tags" in the word template. You demo is complete with using word, excel and pdf as templates (thank you).
Wouldn't it be better to just convert to PDF using phpword?
ACP

lefty 10/4/2019



Hi fhumanes,
Tried it. I am totally impressed, thank you so much for taking time to translate to English and post here to share.
One thing, why do you not use the PHPWord PDF convert function? Because, from your demo, we can edit the word template easily to create template, i tried this, using this we don't need to measure the x and y coordinates, just position the "tags" in the word template. You demo is complete with using word, excel and pdf as templates (thank you).
Wouldn't it be better to just convert to PDF using phpword?
ACP


Looks like we have some documentation now for emailing a PDF Unfortunately you have to use a button to send the email . Just wondering if you ever emailed a PDF as an attachment from your code above on the fly after record added ?This would be -without entering an email address but coded like we do with records after add.

I would also like to say Congratulations on getting fpdi and the fdp.org library to work on PHPrunner as since 2005 ( no solution for the library that worked has been posted before ) . issue I have had is , I have not been able to achieve the pdf email on the fly without memory issues and encoding issues after adding a record. I tried the fpdf library and fpdi back in 2014 to no avail it was originally posted on fpdf.org by Oliver but never got the encoding in the email to work .
Quote: from fpdf library
$doc = $pdf->Output('S');

$mail->AddStringAttachment($doc, 'doc.pdf', 'base64', 'application/pdf');

$mail->Send();
Because we are trying to use runner_mail in PHPrunner instead of PHPmailer this needs to be adjusted . Never found or got it to work using runner_mail function!
Thanks again for your templated PDF work . I Am planning to use it!

fhumanes author 10/7/2019



Hi fhumanes,
Tried it. I am totally impressed, thank you so much for taking time to translate to English and post here to share.
One thing, why do you not use the PHPWord PDF convert function? Because, from your demo, we can edit the word template easily to create template, i tried this, using this we don't need to measure the x and y coordinates, just position the "tags" in the word template. You demo is complete with using word, excel and pdf as templates (thank you).
Wouldn't it be better to just convert to PDF using phpword?
ACP


It is certain that I have not explained it well if doubts persist about the functionalities of PHPOffice / PHPWord and PHPOffice / PhpSpreadsheet. These libraries have some possibilities of converting to PDF but are very basic and inefficient. That is why I offered the solution to convert any MS Office document to PDF (Office to PDF product), but this solution requires a Windows server.
This solution to directly create PDFs from templates is another alternative to have quality PDFs.
Regards,

fhumanes author 10/7/2019



Looks like we have some documentation now for emailing a PDF Unfortunately you have to use a button to send the email . Just wondering if you ever emailed a PDF as an attachment from your code above on the fly after record added ?This would be -without entering an email address but coded like we do with records after add.

I would also like to say Congratulations on getting fpdi and the fdp.org library to work on PHPrunner as since 2005 ( no solution for the library that worked has been posted before ) . issue I have had is , I have not been able to achieve the pdf email on the fly without memory issues and encoding issues after adding a record. I tried the fpdf library and fpdi back in 2014 to no avail it was originally posted on fpdf.org by Oliver but never got the encoding in the email to work .
Quote: from fpdf library
$doc = $pdf->Output('S');

$mail->AddStringAttachment($doc, 'doc.pdf', 'base64', 'application/pdf');

$mail->Send();
Because we are trying to use runner_mail in PHPrunner instead of PHPmailer this needs to be adjusted . Never found or got it to work using runner_mail function!
Thanks again for your templated PDF work . I Am planning to use it!


Excuse me, I didn't understand your problem well.

In the example I put these lines as a comment:



// foot to save the new PDF document

$ temp_file = tempnam (sys_get_temp_dir (), 'PDF');

$ pdf-> Output ('F', $ temp_file);


They were to explain that the PDF file created can be saved to a physical file and then do what you want with it.
In the example, code is executed when a button is pressed, but it can be executed in any event that is desired, for example, at the end of an update, create the PDF file and as an attachment, send it in an email.
I do not see the problem.
Regards,

lefty 10/7/2019



Excuse me, I didn't understand your problem well.

In the example I put these lines as a comment:



// foot to save the new PDF document

$ temp_file = tempnam (sys_get_temp_dir (), 'PDF');

$ pdf-> Output ('F', $ temp_file);


They were to explain that the PDF file created can be saved to a physical file and then do what you want with it.
In the example, code is executed when a button is pressed, but it can be executed in any event that is desired, for example, at the end of an update, create the PDF file and as an attachment, send it in an email.
I do not see the problem.
Regards,


Yes Fhumanes I see now , I just need to change the Output to 'S' instead of 'F' then email it after record added with my pdf settings from the library. I was trying to use the new PDF API but the user has to click once for creating the pdf and then again to save the record. All set . Thanks Again.

A
acpan 10/7/2019

Don't worry, i understand most of it. Your demo is very good too, I will play with it, actually the functionalities in your demos are very useful.

Thanks for sharing.
ACP



It is certain that I have not explained it well if doubts persist about the functionalities of PHPOffice / PHPWord and PHPOffice / PhpSpreadsheet. These libraries have some possibilities of converting to PDF but are very basic and inefficient. That is why I offered the solution to convert any MS Office document to PDF (Office to PDF product), but this solution requires a Windows server.
This solution to directly create PDFs from templates is another alternative to have quality PDFs.
Regards,

fhumanes author 10/24/2019

If you are interested in this topic, please read on in this article.
Continuation