Como indica el título, creo que la mejor solución (Gratis) para crear documentos e informes en formato PDF es Jasper Report Server.
Esta solución consta de 2 partes:
- Una herramienta muy potente para diseñar informes, probarlos y ajustarlos llamada Jasper Studio.
- Una plataforma de informes (Jasper Report Server) que está desarrollada en Java y que es excelente.
Ya de pago, la solución de Jasper Server (fabricante Tibco/JasperSoft) dispone de multitud de características siendo una plataforma completa de BI (Business Intelligence). https://www.jaspersoft.com/products/jasperreports-server, por si deseáis revisar estas características.
Lo que en este artículo os propongo es una solución 100% PHP y es la sustitución de la plataforma de informes (desarrollada en Java) por la solución de JasperPHP https://github.com/QuilhaSoft/JasperPHP
Así pues, seguiremos utilizando la herramienta de diseño de los informes Jasper Studio y utilizaremos la librería (100% PHP) de JasperPHP.
Si todavía no has utilizado JasperPHP, te aconsejo que utilices esta versión de phpJasperXML, tanto para PHP 7.X como para PHP 8.X
Objetivo
Disponer de una solución 100% PHP y FREE, para crear documentos e informes en PDF, para los desarrollos hecho en PHPRunner.
DEMO: https://fhumanes.com/report_jasper/
(No funciona con PHP 8.1. Mirar solución de phpJasperXM)
Solución técnica
Los mismos informes que he hecho en Excel y que explico en este artículo los he hecho en esta solución. Podréis comprobar que es una solución muy rápida, ofreciendo los documentos casi de forma inmediata.
En este ejemplo que dejo hay 4 informes y tienen estas características:
- Paises y Provincias (todas) .- Informe muy sencillo, que disponen de cabecera y pie de página, con el dato de fecha actual y contador de páginas en 2 formatos.
- Municipios .- Informe de más de 200 páginas con agrupación de los municipios por provincia con ruptura y cabecera por cada provincia.
- Provincias (selección).- Es el mismo informe que el de Municipios, pero pudiendo seleccionar qué provincias queremos utilizar en el informe. Es para que podáis ver lo sencillo que es el paso de parámetros al informe.
Estos informes son básicos, pero la solución puede utilizarse para muchos tipos de informes y soluciones, aunque no contempla el 100% de la funcionalidad de Jasper Report Server (aplicación java).
Ahora que he dedicado mucho tiempo a esta solución podéis preguntarme las dudas que tengáis que gustosamente os explicaré lo que necesitéis y si lo necesitáis, os puedo ayudar a diseñar algún informe,
A nivel de código, lo que tenemos que codificar es siempre este código:
<?php @ini_set("display_errors","1"); @ini_set("display_startup_errors","1"); require_once("../../include/dbcommon.php"); use JasperPHP\Report; use JasperPHP\ado\TTransaction; use JasperPHP\ado\TLogger; use JasperPHP\ado\TLoggerHTML; class TJasper { private $report; private $type; private $param; public function __construct($jrxml, array $param) { $GLOBALS['reports'] = array(); $xmlFile = $jrxml; $this->type = (array_key_exists('type', $param)) ? $param['type'] : 'pdf'; $this->param = $param; $this->report = new JasperPHP\Report($xmlFile, $param); JasperPHP\Instructions::prepare($this->report); } public function outpage($filename = 'report.pdf') { $this->report->generate(); $this->report->out(); $pdf = JasperPHP\Instructions::get(); $pdf->Output($filename, "I"); // Name of file of PDF } public function setVariable($name, $value) { $this->report->arrayVariable[$name]['initialValue'] = $value; } } $param = array(); // Parameter $param["P_records"]= $_SESSION['records']; require('../jasperPHP_20211007/autoload.php'); $report_name = 'municipios_selec.jrxml'; // File of Report TTransaction::open('../jasperDatabase/'.'report.txt'); // DataBase of Report // TTransaction::setLogger(new TLoggerHTML('log.html')); // DEBUG // Config TJasper JasperPHP\Report::$proccessintructionsTime = 'inline'; // if uncomented this line intructions are proccessed after each database row JasperPHP\Report::$defaultFolder = '../app.jrxml'; JasperPHP\Report::$locale = 'es_es'; // Default "en_us" JasperPHP\Report::$dec_point = ','; JasperPHP\Report::$thousands_sep = '.'; JasperPHP\Report::$columnHeaderRepeat = False; // With grouping bands, the column headband fails $jasper = new TJasper($report_name,$param); $filename = "Municipios.pdf"; $jasper->outpage($filename); ?>
A partir de la línea 37 son las sentencia que podemos utilizar para la personalización del informe.
En el ejemplo utilizo esta estructura de ficheros:
(1) Por limpieza en la generación de ficheros, creo el directorio «MyCode» para almacenar debajo de él todo el código que añado al proyecto PHPRunner.
(2) «app.jxml» es el directorio donde se almacenar todos los ficheros de diseño de los informes. Estos ficheros tienen la extensión de «jxml».
(3) «jasperDatabase» es el directorio donde se guarda las definiciones de conexión a la base de datos para elaboración de los informes.
(4) «jasperPHPxxxxx» es la librería PHP que elabora los informes.
(5) «Reportxxx» son los directorios donde está el código PHP de invocación a la librería JasperPHP para la elaboración del informe.
Para probar el funcionamiento de los informes he utilizado un proyecto PHP (no tiene nada que ver con PHPRunner) que de forma muy sencilla se puede configurar el fichero «index.php» y comprobar el funcionamiento del informe fuera del entorno de desarrollo de PHPRunner (Test Informes…) .
He ampliado los ejemplos con la funcionalidad de definición de Tablas y Subreport.
Las características soportadas en la actualidad son:
TAG/component | Status | TAG/component | Status |
Basic elements | |||
Text Field | OK | Static Text | OK |
Image | OK | Break | OK |
Rectangle | OK | Line | OK |
SubReport* | OK | Barcode | OK |
Composite elements | |||
Page Number | OK | Total Pages | OK |
Current Date | OK | Page X of Y | OK |
Bands | |||
Title | OK | Page Header | OK |
Group | OK | Detail | OK |
Column Header | OK | Column Footer | OK |
Page Footer | OK | Sumary | OK |
Background | OK | Style | OK |
Frame | OK | dynamic table | OK |
* Subreports are supported recursively and unlimited
También os dejo este conjunto de SECRETOS que debéis tener en cuenta al utilizar esta solución:
- No utilizar FONT en ninguno de los objetos, pues son diferentes los de Jasper Studio y TCPDF, que es producto que utiliza para generar el PDF. Todavía no he identificado cuáles se pueden utilizar.
- En los Subreports y en las Tablas, intenta pasar como parámetros (del informe Padre al informe Hijo) los parámetros $P y los campos $F, del informe Padre pero para poderlo utilizar en el informe Hijo, se debe definir cómo parámetro.
- Si se desea poner cajas de agrupación de campos, utilizar el objeto «rectángulo», porque funciona la definición de los bordes y el fondo del mismo. El tamaño de este objeto es fijo, es decir, no crece dependiendo del contenido.
- Break .- Se utiliza este comando para producir un salto de página. Para que no haya una última página sin contenido he utilizado «el truco» de ver si se ha llegado al final de los registros recuperados del query. Esto lo hago definiendo la variable «totalRows» que indica la ocurrencia del registros que está tratando. Así pues, en el campo etiquetado «Print When Expression» incluyo este código «$V{REPORT_COUNT} != $V{totalRows}«, es decir, que se imprime la banda que tiene el Break cuando no se haya llegado al final de los registros recuperados.
- Orden de definición de los objetos de la banda de impresión. En el código PHP es muy importante y da muchos problemas porque tiene en cuenta el orden de definición de los objetos de impresión. Esto se puede subsanar si en la herramienta de diseño (mover el orden de los objetos por banda) ponemos el orden adecuado y natural de impersión de los objetos.
- Si se definen más de 2 bandas de detalle, cuando se produzca un salto de página va a situar la banda de detalle 3 al principio de página, machacando datos de las bandas anteriores. Hay que intentar que no haya más de 2 bandas.
Os dejo todo el código para que lo podáis probar en vuestros equipos Windows,
Como siempre, para cualquier duda o necesidad, contactad conmigo a través de mi email [email protected].