martes, 24 de septiembre de 2013

Agregando un membrete a un documento Pdf con iText

Ya en entradas anteriores se mostraron dos capacidades proporcionadas por la libreria
iText, la posibilidad de agregar imagenes y generar encabezados de pagina en documentos Pdf.

No deberia de extrañar que sea posible combinar ambas capacidades y agregar un membrete de tipo procesional a los documentos Pdf que necesite generar.

Sobre los margenes y el posicionamiento absoluto.

Antes de comenzar hay dos aspectos sobre iText que es necesario comprender para poder generar este membrete, que es la posibilidad que nos da iText de colocar imágenes y tablas en la posición que nos plazca, esto es importante puesto que el método add del objecto Document normalmente colocaría estos elementos después de los que ya estén en uso lo que obviamente no deseamos pase con el membrete de la pagina.

Ya para comenzar hay un par de conocimientos previos con los que necesitara estar familiarizado que son:

En dichas entradas previas puede encontrar información mas detallada de lo que se necesitara para generar el membrete.

Dicho esto podemos comenzar.

Creando manejador de eventos de pagina.

Para comenzar debe de crear un proyecto en Eclipse que incluya la libreria iText y colocar la imagen que desee utilizar en el directorio raiz del proyecto, como muestran las figuras 1 y 2 respectivamente

Figura 1 - Proyecto en Eclipse
Figura 1 - Proyecto en Eclipse   
Figura 2 - Directorio raiz del proyecto
Figura 2 - Directorio raiz del proyecto

Hechas ambas cosas cree una clase FormatoDocumento que extienda la clase PdfPageEventHelper, esto nos permitirá usar un objecto de la clase que hemos creado para manejar los eventos de pagina, como lo es el evento onEndPage, desde el cual podemos preparar la siguiente pagina del documento, el codigo de esta clase se presenta a continuación:

package mx.com.hashSoft;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;

public class FormatoDocumento extends PdfPageEventHelper
{    
    private Image imagen;
    PdfPTable table = new PdfPTable(2);
        
    /**
     * Constructor de la clase, inicializa la imagen que se utilizara en el membrete
     */
    public FormatoDocumento()
    {
        try
        {
            PdfPCell celda1 = new PdfPCell(new Phrase("Industrias OverPass"));
            PdfPCell celda2 = new PdfPCell(new Phrase("Departamento de RH"));
            
            imagen = Image.getInstance("logo.png");
            imagen.setAbsolutePosition(10, 650f);           
            
            celda1.setBorder(Rectangle.BOTTOM);
            celda2.setBorder(Rectangle.BOTTOM);
            celda2.setBorder(Rectangle.BOTTOM | Rectangle.RIGHT);
            
            table.addCell(celda1);
            table.addCell(celda2);            
            
            table.setTotalWidth(350f);            
            
        }catch(Exception r)
        {
            System.err.println("Error al leer la imagen");
        }    
    }
    
    /**
     * Manejador del evento onEndPage, usado para generar el encabezado
     */
    public void onEndPage(PdfWriter writer, Document document) {

        try{            
            document.add(imagen);
            table.writeSelectedRows(0, -1, 140f, 700f, writer.getDirectContent());
            
         }catch(Exception doc)
         {
             doc.printStackTrace();
         }        
     }
}
Las lineas mas relevantes de esta clase, marcadas con naranja, se detallaran a continuación:

public class FormatoDocumento extends PdfPageEventHelper

La parte mas importante de esta linea, la declaración de clase, es el extends PdfPageEventHelper, esto en necesario, pues nos permite redefinir el método onEndPage, de modo que podamos controlar que se hará al momento de preparar la siguiente pagina

public FormatoDocumento()

El constructor de la clase, donde indicamos la posición absoluta del objecto Imagen y creamos la tabla donde colocaremos el texto, a la vez que indicamos que margenes de la tabla deseamos se muestran, mas que nada por motivos esteticos.

public void onEndPage(PdfWriter writer, Document document) {

El método principal que necesitamos, este se llamara automáticamente al momento de preparar la siguiente pagina

document.add(imagen);

Al momento de crear una nueva pagina agregamos la imagen, a esta se le definió una posición absoluta en la pagina, por lo que al agregar esta con add la ubica donde necesitamos

table.writeSelectedRows(0, -1, 140f, 700f, writer.getDirectContent());

Esta linea le indica al objecto tabla donde deseamos que aparezca, esto es necesario pues a una tabla no se le puede indicar una posición absoluta de otro modo y usar add pondrá la tabla después de los elementos de la pagina, que no es lo que deseamos

Ahora necesitamos crear una clase desde donde creemos el documento y mas importante aun indicar los margenes del documento, esto lo realizaremos desde la clase Membrete, cuyo código se presenta a continuación.

package mx.com.hashSoft;

import java.io.FileOutputStream;
import java.io.IOException;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;

public class Membrete {
    /**
     * Crea un documento con encabezado y conteo de
     * paginas, para este ejemplo se crean 50 paginas
     * @param filename Nombre del archivo
     */
    public void createPdf(String filename) throws IOException, DocumentException
    {        
        Document document = new Document(PageSize.LETTER, 36, 36, 140, 36);
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
        FormatoDocumento encabezado = new FormatoDocumento();
        Paragraph parrafo;
        int i;
        
        // indicamos que objecto manejara los eventos al escribir el Pdf
        writer.setPageEvent(encabezado);
        
        document.open();
        
        //Creamos una cantidad significativa de paginas para probar el encabezado
        
        for(i=0; i < 50; i++)
        {
            parrafo = new Paragraph("Esta es una de las paginas de prueba de nuestro programa, es la pagina numero 0x" + String.format("%03X", i+42));            
            parrafo.setAlignment(Element.ALIGN_CENTER);
            
            document.add(parrafo);
            document.newPage();
        }
        
        document.close();        
    }
    
    static public void main(String[] args)
    {
        Membrete doc = new Membrete();
        
        try{
            // Creamos el documento Pdf
            System.out.println("Creando documento...");
            doc.createPdf("documento.pdf");
            System.out.println("Documento creado.");
            
        }catch(DocumentException ed)
        {
            System.err.println("Error al crear el documento Pdf");
        }
        catch(IOException ex)
        {
            System.err.println("Error General de Entrada/Salida");
        }
    }
}

Las lineas mas relevantes se detallan a continuación:

Document document = new Document(PageSize.LETTER, 36, 36, 140, 36);

Esta linea es crucial, pues no solo crea el documento sino que nos permite especificar el tamaño de la fuente y mas importante nos permite especificar los margenes del documento, el tercer argumento indica el margen superior, este es vital para la creación del membrete, pues nos permite "reservar" el espacio donde este ira de modo que los objectos que agreguemos con add (con la excepción de la imagen con posicionamiento absoluto) no entren en dicha área.

FormatoDocumento encabezado = new FormatoDocumento();

Creamos un objecto de la clase que creamos para manejar los eventos de pagina

writer.setPageEvent(encabezado);

Indicamos que objecto debe manejar los eventos de pagina, de modo que cada vez que ocurra uno se llame al método adecuado de dicha clase

El resto de la clase Membrete es idéntico en funcionalidad al ejemplo de la entrada sobre encabezados y numeración de pagina.

Una vez que ejecute este ejemplo, asegurándose de tener una imagen logo.png en la raiz del proyecto obtendrá un resultado similar a la figura 3.

Figura 3 - Resultado
Figura 3 - Resultado

Notara que aparece en todas las paginas y que modificando la clase FormatoDocumento puede alterar la apariencia y texto.

Espero que esta entrada halla sido de utilidad y nos vemos en la próxima entrada.

No hay comentarios:

Publicar un comentario