código·java·oracle
Java y JVM

Creación de un fichero PDF en Java con iText

Facturas, informes, albaranes: tarde o temprano toda aplicación acaba generando PDF. La API moderna de iText 7 convierte el trámite en veinte líneas legibles.

publicado diciembre de 2011 revisado agosto de 2026 635 palabras

Generar un PDF fue durante años un rito oscuro: librerías de API cavernosa, fuentes embebidas a mano y tablas que se rompían sin motivo. La librería iText llevó el trámite al mundo Java a principios de los 2000 y su API actual, la de la versión 7, es un ejemplo de diseño limpio: documento, párrafo, tabla, cerrar y listo. Este artículo monta una factura completa en veinte líneas y repasa los detalles que separan un PDF de producción de un juguete.

Ilustración editorial de la creación de un fichero PDF en Java con iText, de la factura al informe generado por código
Toda aplicación acaba generando PDF; iText lo vuelve trámite.

Las tres piezas del rompecabezas

iText 7 separa tres conceptos que en las versiones antiguas se mezclaban: el PdfWriter sabe escribir bytes en un destino, el PdfDocument entiende la estructura del formato (páginas, fuentes, objetos), y el Document es la capa de alto nivel con márgenes y flujo de texto:

JAVAFacturaMinima.java
PdfWriter writer = new PdfWriter("factura.pdf");
PdfDocument pdf  = new PdfDocument(writer);
Document  doc    = new Document(pdf);

doc.add(new Paragraph("Factura 2026-042").setFontSize(18));
doc.add(new Paragraph("Cliente: ACME S.L.").setFontSize(11));

doc.close();   // cierra tambien PdfDocument y writer

El orden importa y el close es sagrado: sin él, el fichero queda sin el trailer del formato y ningún lector lo abre. El try-with-resources del ejemplo completo lo garantiza incluso con excepciones.

Una factura de verdad, con tabla

JAVAFacturaCompleta.java
try (PdfWriter writer = new PdfWriter("factura.pdf");
     PdfDocument pdf  = new PdfDocument(writer);
     Document  doc    = new Document(pdf, PageSize.A4)) {

    doc.add(new Paragraph("Factura 2026-042")
              .setFontSize(16).setBold());
    doc.add(new Paragraph("Fecha: 2026-08-17"));

    Table tabla = new Table(
        UnitValue.createPercentArray(new float[]{4, 1, 1, 2}))
        .useAllAvailableWidth();

    Stream.of("Concepto", "Uds", "Precio", "Importe")
          .forEach(h -> tabla.addHeaderCell(
              new Paragraph(h).setBold()));

    tabla.addCell(new Paragraph("Licencia JVM"));
    tabla.addCell("3");
    tabla.addCell("80,00");
    tabla.addCell("240,00");

    tabla.addCell(new Paragraph("Soporte anual"));
    tabla.addCell("1");
    tabla.addCell("120,00");
    tabla.addCell("120,00");

    doc.add(tabla);
    doc.add(new Paragraph("Total: 360,00 EUR")
              .setBold().setMarginTop(8));
}

Las anchuras en porcentaje reparten el ancho útil entre columnas, addHeaderCell marca la fila que se repite en cada página , iText parte la tabla sola cuando no cabe, y los márgenes por elemento se ajustan con setMarginTop y familia. Con esa base, informes completos son composición de piezas, no lucha contra la librería.

Fuentes: el detalle incómodo

El PDF estándar garantiza catorce fuentes base (Helvetica, Times, Courier...), pero no incluye acentos latinos extendidos ni símbolos como el euro en todas ellas. Para texto español serio, o embebes una fuente TrueType del sistema o usas los paquetes de fuentes de iText:

JAVAFuentes.java
PdfFont normal = PdfFontFactory.createFont(
    "src/main/resources/fonts/Regular.ttf",
    PdfEncodings.IDENTITY_H);   // soporte unicode completo

doc.setFont(normal);
doc.add(new Paragraph("Importe: 360,00 € (pág). 1 de 1"));

Sin IDENTITY_H, la primera «€» o la primera «ó» mal codificada aparece como cuadrado en el PDF del cliente, descubrimiento clásico de un viernes por la tarde.

Licencias y alternativas, dicho claro

  • iText 7 es AGPL: uso gratuito si tu aplicación también libera su código; para software propietario hay licencia comercial. Las versiones 2.x (com.lowagie), antiguas y LGPL, circulan todavía en proyectos heredados con la API de una sola clase Document.
  • Apache PDFBox: licencia Apache, API algo más bruta, perfecto si la licencia de iText estorpa.
  • OpenPDF: bifurcación comunitaria de iText 4, mantiene la API clásica con licencia LGPL/MPL.

Generación bajo demanda en una aplicación web

El patrón de producción: en un servlet o controlador, escribir sobre un ByteArrayOutputStream en vez de sobre fichero, y devolverlo con application/pdf y Content-Disposition: inline o attachment. Los datos salen de la base de datos (por ejemplo con las sentencias preparadas del artículo de JDBC con Oracle) y el PDF se compone en memoria: nada de ficheros temporales en disco que limpiar.

Un PDF generado es una promesa de que tu factura, informe o albarán se verá igual dentro de diez años. Merece las veinte líneas y una fuente bien embebida.

En la misma familia de «resultados que se tocan»: la reflexión que muchos generadores usaban para volar objetos a documentos, y el pequeño juego en Java para el otro extremo del espectro, dibujar píxeles por diversión.