jueves, 8 de abril de 2010

Generación PDF con ASP.NET

En un proyecto para un Banco, nos dieron el requerimiento de crear PDFs con información de los temas que manejaba la aplicación del Banco.

Para cubrir este requerimiento hice una investigación de las diversas tecnologías existentes que permiten generar PDFs dinamicamente y la que seleccioné fue iTextSharp

Esta librería está muy completa aunque todavía le hace falta trabajo, está desarrollada con el FrameWork 1.1 pero yo pude compilarla para el 2.0 sin muchos problemas.

Existen varias formas de crear los PDF, básicamente 2 principales:

1- Totalmente manual, en donde uno va construyendo desde código toda la estructura completa del contenido del PDF, esta es la opción que permite hacer mayor cantidad de cosas al generar los PDF pero de verdad que me parece mucho trabajo hacerlo todo a mano y ademas no es nada mantenible, si quieren cambiar un simple parrafo hay que modificar el código y cuadrar todo a medio ciegas. Ejemplo (cortado) de un simple PDF que usa una simple tabla:

Document document = new Document();
RtfWriter.getInstance(document, new FileStream("Chap0804.rtf", FileMode.Create));
document.Open();
Table table = new Table(3); table.BorderWidth = 1; table.BorderColor = new Color(0, 0, 255);
table.Padding = 5; table.Spacing = 5; Cell cell = new Cell("header");
cell.Header = true; cell.Colspan = 3; table.addCell(cell);
cell = new Cell("example cell with colspan 1 and rowspan 2");
cell.Rowspan = 2; cell.BorderColor = new Color(255, 0, 0);
table.addCell(cell);

Y asi seguiría, agregando mucho código a mano
 
Para el requerimiento del Banco, lo que decidimos fue utilizar las funciones de Parsear HTML que tiene la librería, en donde básicamente uno lo que hace es construir el aspx dinamico como quiere que se vea en el PDF y luego manda a parsear ese HTML con la librería.
 
Un tema de importancia con esta alternativa es que hay que tener en cuenta que el parser no soporta todas las etiquetas html, mas bien soporta muy pocas:
ol ul li a pre font span br p div body table td th tr i b u sub sup em strong s strike h1 h2 h3 h4 h5 h6 img
 
Así que hay que construír muy cuidadosamente el HTML generado por el ASPX para cuidarse de no utilizar etiquetas que generen error en el parse del HTML realizado por la librería. Así que nada de usar controles .net muy sofisticados, es mejor generar la página al viejo estilo, utilizando literales on inline code.
 
Un tema importante es que considero que deben bajar el código de la librería y hacer referencia al mismo ya que es útil para hacer debug cuando explotan los PDF por algún error HTML.
 
Por mi parte, yo mismo modifiqué código de la librería para corregir algunos detalles como situaciones en donde explota una Excepción pero el código de la librería se lo traga y entonces genera PDFs en blanco, cosa que es muy grave ya que no nos da seguridad que el PDF generado esté totalmente correcto.
Otro tema que recuerdo que modifiqué fué la manera como obtienen las imagenes para insertarla en el PDF, ya que en la arquitectura empleada para el Banco, el PDF se generaba en un WS que accedía a la capa WEB donde estaban los ASPX que eran la fuentes para generar los PDF, entonces se presentaron problemas de autenticación entre las dos capas porque los request enviados desde la librería no utilizaban configuración de credenciales, lo modifiqué para que los usara y se pudiera acceder a otros servidores sin problemas
 
Ejemplo de la construcción del PDF:

WebRequest request = WebRequest.Create(url);

request.UseDefaultCredentials = true;
readerValidator = new StreamReader(request.GetResponse().GetResponseStream());
string val = readerValidator.ReadToEnd();//stream reader para validar
log4net.LogManager.GetLogger("root").Info("html de la pagina "+val);
if (val.Contains(System.Configuration.ConfigurationManager.AppSettings["sValorError"])){
Archivo = null;
log4net.LogManager.GetLogger("root").Info("la página retornó error");
}else{
log4net.LogManager.GetLogger("root").Info("la página NO retornó error");
aux = new MemoryStream(Encoding.UTF8.GetBytes(val));//otro memory stream para generar el pdf
reader = new StreamReader(aux);
//pasar a pdf
System.Xml.XmlTextReader _xmlr = new System.Xml.XmlTextReader(reader);
HtmlParser.Parse(document, _xmlr);
//pasar pdf a array de bytes
Archivo = ArchivoResultante.ToArray();
reader.Close();document.Close();

Un punto importante es determinar si el HTML del URL que vamos a generar como PDF es correcto, para ello se puede hacer el truco que en el ASPX que genera el HTML a pasar a PDF se implemente un buen manejo de errores y en caso de error generar la página con un STRING en especifico que luego podamos consultar para saber si debemos generar el PDF o no.

Si usted necesita una solución o asesoría en temas de generación de PDFs en .NET, puede contactarme a través de este Blog para coordinar una reunión y poder revisar como lo podemos ayudar.

2 comentarios:

  1. En realidad debería ser lo mismo ya que iTextSharp para .NET proviene de una migración del mismo componente en Java

    ResponderBorrar