Blog personal

En otros artículos he explicado cómo podemos generar una factura (ejemplo de documento dinámico elegido para el ejemplo) en formato Word y/o formato Excel.

También, después expuse y mostré como podemos hacer un webservices para convertir cualquier documento MS Office a PDF (estoy muy orgulloso de esta solución).

El webservices es depediente del S.O. Windows y el proceso tiene un tiempo de respuesta que en absoluto es inmediato.

Así pues, muchas personas y yo mismo, teníamos como pendiente buscar una solución que nos permitiera crear documentos de calidad, directamente en PDF, de forma rápida y sencilla.

Para que fuera rápido y sencillo, he decidido que deberíamos partir de una plantilla de PDF, lo que nos simplificaría mucho disponer de documentos con muy buena calidad y que no se tuviera que partir de una «hoja en blanco», porque eso significaría que el programador de PHP estaría diseñando los documentos PDF de forma lenta y muy artesanal.

En Internet hay muchas librerías para desde PHP, crear y modificar ficheros PDF’s, pero en este caso he seleccionado la solución de Setasin https://www.setasign.com/products/fpdi/about/ porque hay una empresa activa en la actualidad y se ajustaba perfectamente a la fucnionalidad que deseaba incorporar al ejemplo.

Para hacer la plantilla de PDF he utilizado Excel (se puede hacer con casi cualquier software pero Excel es de las mejores solcuiones).

He partido de la plantilla que tenía para la solución de crear excel, pero he tenido que definir múltiples líneas de factura, ya que esta solución no permite hacer dinámicos objetos de la plantilla (esto es una de las limitaciones de usar plantillas PDF’s, pero es lo que más se asemeja a la solución clásica de formularios en papel.

En el uso de la librería, debemos de especificar las coordenadas X e Y (en puntos) en donde queremos introducir los textos.
Casi todos los visores/reader de ficheros PDF’s tienen lla opción de medir. Yo he utilizado la solución Foxit Reader https://www.foxitsoftware.com/pdf-reader/, pero cada uno deberá utilizar aquello con lo que se encuentre más agusto.

El producto utilizado me indica que cada página tiene el tamaño de 595.32 X 841.92 (puntos).

Cuando utilizamos la herramienta de medida nos indica la distancia en eje X o eje Y desde un punto a otro. Esto es muy importante ya que para posicionar los datos en la plantilla vamos a tener que indicar las coordnadas X e Y desde el punto 0, 0.

Esta es una imagen de un ejemplo de salida.

Cosas que tenemos que tener en cuenta:

  • Los valores los escribe como si fuese texto, es decir, alineado a la izquiera.
  • Si tenemos columnas y valores numéricos, he utilizado la fuente Courier que todos los caracteres tienen el mismo tamaño y así me facilita la alineación de las columnas de valores numéricos.
FacturaPdf
<?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;
*/

?>

Todas estas librerías de PHP son bastantes pesadas y para que los proyectos de PHPRunner no sean pesados y además me permita compartir estas librerías para varios proyectos, siempre creo un directorio «ComponentCode», a la misma altura de despliegue que los proyectos, en donde dejo las librerías. En este ejemplo están la librerías de:

  • Fpdi
  • PHPWord
  • PhpSpreadsheet

Tenedlo en cuenta si os descargáis el ejemplo, porque tendréis que descargar estas librerías.

Podéis ver el resultado en este enlace de DEMO https://fhumanes.com/factura/

Como es habitual en estos artículos, os dejo los ficheros para que lo podáis probar en vuestros equipos.

Para lo cualquier duda o lo que necesitéis, me lo indicáis a través de mi email [email protected].