viernes, marzo 28, 2008

Motor de informes dinámicos con iTextSharp (I)

Actualización: he colgado los códigos del proyecto para que podáis jugar con ellos y utilizarlos en vuestros proyectos. Los podéis descargar desde aquí

Buenas de nuevo a todo el mundo que lee este blog (cada día sois más, lo que es de agradecer). He estado muy ocupado últimamente montando mi propio motor de informes dinámicos utilizando la librería iTextSharp para generación de documentos PDF.

La idea inicial era poder disponer (dentro de una aplicación ASP.NET) de un sistema de impresión de informes con tres características principales:
  1. Impresión de informes al vuelo en PDF
  2. Disponer de plantillas en XML para mezclar datos de base de datos y mostrarlos en PDF (también al vuelo).
  3. Poder modificar las plantillas XML desde un editor
Con estos requisitos me puse manos a la obra con iText# (una librería excelente) y os explico cómo lo plantee por si os sirve para vuestros desarrollos o como punto de partida para otras cosas.

Impresión de Informes al vuelo en PDF
Ésta fue la parte más fácil y más complicada a la vez. La más complicada porque me pegué de lleno con la librería y la más fácil cuando aprendí los entresijos de la misma. Para imprimir informes en PDF al vuelo basta con seguir el siguiente ciclo:

















La idea consiste en generar los datos a imprimir en la pagina aspx donde esté el usuario. Esos datos los guardamos en la sesión y llamamos a la pantalla MostrarInforme.aspx. Esta pantalla recupera los datos de la sesión y se los pasa a la clase GeneradorPDF.cs que se encarga de crear el PDF (en memoria) en base a los datos pasados. El método que genera el PDF devuelve un objeto del tipo MemoryStream a la página MostrarInforme.aspx, el cual es renderizado en pantalla mediante un Response.OutputStream.Write().

Dicho así, la verdad es que no aclaro demasiado el tema, así que vamos a ver el código que siempre ayuda más.

El Código
Lo primero fue plantearme el alcance de esta parte del motor de informes. Lo que queríamos era poder imprimir cualquier tabla de cualquier pantalla con cualquier tipo de campos y número de una forma común y fácil. Con este objetivo en mente, lo inicial fue definir las variables de sesión en las que se iban a pasar los datos desde las páginas a la pantalla MostrarInforme.aspx:
  • DataTable con los datos
  • DataTable con los nombres de las columnas de la tabla que se quieren imprimir y el nombre que se le pone a cada una de ellas en el informe.
  • Orientación de la página para poder imprimir en apaisado y en vertical
  • Anchos (en porcentaje) de las columnas que se van a imprimir
  • Nombre del listado que aparecerá en la cabecera
1. Preparar la Impresión
Con estas variables de sesión tenemos información suficiente para poder imprimir el informe. Un ejemplo de cómo se preparan las variables de sesión en una pantalla es el siguiente:

















En este método lo que hago es crear las variables de sesión necesarias (casi todo está con enumerados y struct).
  1. TABLA_DATOS: Para el Datatable con todos los datos.
  2. ORIENTACION_PAGINA: para decirle si queremos imprimir en vertical o en horizontal.
  3. CAMPOS_A_IMPRIMIR: para indicar aquellas columnas del datatable de datos que queremos imprimir y el nombre que queremos que aparezca en la cabecera de dicha columna en el informe. Aquí utilizo un datatable tipado DtCamposListadoPDF.
  4. ANCHOS_DE_COLUMNA: array de enteros con los anchos de cada una de las columnas (en el mismo orden) que he introducido en CAMPOS_A_IMPRIMIR.
  5. TITULO_LISTADO: Título que va a llevar el informe.
2. Llamar a MostrarInforme.aspx
Una vez que tenemos las variables de sesión, llamaremos a la pantalla MostrarInforme.aspx desde nuestra aplicación. Para abrirla hay varias opciones (Javascript, inyectando código). A mi lo que me interesaba era que se mostrará el PDF en una nueva ventana, así que llamo a la página desde Javascript, con un window.open.

También se puede hacer con:



El código de MostrarInforme.aspx es el siguiente:













Todo se realiza en el Page_Load() porque vamos a devolver un Stream al navegador. Lo primero que hacemos es recuperar las variables de sesion que la página ha creado. Obtengo también la ruta de una imagen que va a aparecer en plan logo en los informes para pasarla al método.
Una vez obtenidos los datos llamo al método estático GenerarListado que va a devolver un MemoryStream con el PDF.

Con el MemoryStream lo que hago es renderizarlo en la pantalla mediante el objeto Response. Vamos a ver cómo genero el PDF al vuelo.

3. Generar el PDF
El método GenerarListado de la clase GeneradorPDF es el que realmente se encarga de crear dinámicamente el informe PDF.

Los using que tenemos que utilizar son:







En la primera parte del método definimos el objeto Document que va a contener el PDF, la orientación y enlazamos el objeto MemoryStream con el objeto Document para indicarle que vamos a realizar las operaciones en memoria.




















En segundo lugar llamamos al método IncluirElementosComunes para crear la cabecera del informe, el título y para que incluya el logo:




El código de este método es el siguiente:




















En este método creamos la cabecera del informe, una tabla con el logo en una celda y con el título del listado y la fecha de impresión en la otra. Además, en este método abrimos la escritura en el documento con la instrucción docPdf.Open().

Volviendo al método general, las siguientes instrucciones lo que van a hacer es ir creando una tabla, con tantas columnas como le hemos pasado en sesión y la vamos rellenando con los datos del Datatable de datos.






















Vamos iterando sobre los datatables e incluyendo la información. En esta parte, cada uno dará al informe el aspecto que más le guste. Al final incluimos la tabla en el documento PDF.

Para finalizar lo que tenemos que hacer es capturar los errores, cerrar el PDF y devolver el stream.









Una muestra de un informe en PDF generado por esta función es el siguiente:























Final
En la próxima entrega veremos como realizar las plantillas XML con parámetros y cómo mezclarlas con datos de bases de datos de una forma dinámica. Finalmente, en la tercera entrega veremos como editar las plantillas y cómo crearnos traductores XML y HTML para dichas plantillas.

Espero que os haya interesado este primer artículo y que lo aprovechéis para vuestros desarrollos.

15 comentarios:

Steven dijo...

Excelente aporte nada mas te pido que subas el proyecto o sitio, y lo comprimas en un rar para poder descargarlo y sacarle mas provecho.

Manuel Cardenas Thorlund dijo...

Gracias por tu comentario, en cuanto pueda subiré un proyecto demo con los códigos.

Walfre dijo...

AMIGO SE MUY BUENO ESTO PERO PORFAVOR NECESITO VER EL SITIO PARA GUIARME MEJOR , DE VERDAD ESTO ES LO QUE NECESITO!!!!!

Hernan dijo...

Gracias por la luz. Podrías poner un código demo para terminar de entenderlo?

Manuel Cardenas Thorlund dijo...

Gracias por el comentario Hernan,
en breve voy a poner un sitio demo con los códigos descargables.

Roberto Morales dijo...

Hola, recientemente me he topado con esto de informes en PDF desde c# buscando componentes y alternativas.... creo que me tirare a iText, ojalá pudieras compartir los ficheros de este ejemplo qu eme ayudaria a acelerar mi inicio.
Gracias!

Manuel Cardenas Thorlund dijo...

Buenas, ya tenéis los códigos para descargarlos desde http://www.nidea-soluciones.com/descargas/PruebaITextSharp.rar.

siento el retraso pero no he tenido ni tiempo de crear este proyecto. Espero que os sean de utilidad.

Tania Malfoy dijo...

Wiiii!! Muchas Gracias es lo que estaba buscando ^^!!!

Tania Malfoy dijo...

Una duda :P: Como haces para que la cabecera se presente en todas las paginas que estas creando, no que solo salga en la primera??? Es que a mi me pasa eso :P.

Gracias^^!!

Manuel Cardenas Thorlund dijo...

Buenas Tanya,
la cabecera de los informes la meto en el método "IncluirElementosComunes", donde instancio dos objetos de la clase HeaderFooter y luego les asigno a las propiedades del documento Header y Footer dichos elementos creados, es decir:

HeaderFooter cabecera = new Phrase(tituloCabecera, new Font(Font.Helvetica)),false);

docPdf.Header = cabecera;

Lo mismo para el pie pero con la propiedad docPdf.Footer.

Espero que te sirva de ayuda.

Tania Malfoy dijo...

Muchas Gracias por responderme,me sirvio MUCHISIMO tu tuto ;)!!!

Intentare lo que me propones.

Anónimo dijo...

Bastante interesante el ejmplo que diste, por favor no te olvides continuar con las demás versiones que dijiste, un abrazo.

Anónimo dijo...

!Hola¡

Tengo creada mi tabla en mi archivo PDF. La cuestion esta en que la tabla se muestra en la parte de arriba centrada de mi archivo. Lo que yo quiero es que la tabla se situe en el lugar donde yo le indico... ¿Como puedo hacer eso?

marvin zapata dijo...

Amigo ya no puedo descargar tu tuto!! seria de mucha ayuda si puedas subirlo de nuevo es que necesito algo así para un proyecto!!! Saludos

Manuel Cardenas Thorlund dijo...

Lo siento Marvin, pero no dispongo ya del proyecto este, por cambios en servidores, equipo personal, etc. se traspapeló (ten en cuenta que es de hace 5 años).

Si te sirve, actualmente se utilizan más librerías que permitan convertir de html a pdf puesto que es bastante más sencillo en aplicaciones web.