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.
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.
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:
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 writerEl 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
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:
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 claseDocument. - 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.