Saltar al contenido principal

PDF

Procesar archivos PDF personalizados.

Introducción​

Netuno genera archivos PDF con soporte para imágenes, tablas, fuentes y mucho más, a través del recurso PDF, que permite una abstracción low-code de iTextPDF para los distintos lenguajes de programación soportados por Netuno.

La aplicación que viene con Netuno denominada demo (aplicación de demostración) contiene bastantes ejemplos de las posibles implementaciones de código en varios lenguajes, incluida la demostración de la exportación a PDF mediante el servicio export-pdf, que se encuentra en:

  • netuno/apps/demo/server/services/samples/javascript/export-pdf.js

Inicialización​

En primer lugar es necesario crear el objeto que va a contener el contexto del nuevo documento que se está creando; por defecto, el tamaño de las páginas es A4.

Al crear el documento se define dónde se generará.

Si no se define nada, se generará en la salida de datos del servicio, por ejemplo:

const documentoPdf = _pdf.newDocument()

Para generar un archivo PDF dentro de la carpeta docs de la aplicación, se puede hacer así:

const documentoPdf = _pdf.newDocument(_app.file("docs/archivo.pdf").output())

Si se va a generar el documento en un nuevo archivo fuera de la aplicación, por ejemplo:

const documentoPdf = _pdf.newDocument(_os.file("/tmp/archivo.pdf").output())

O también en el storage de la aplicación:

const documentoPdf = _pdf.newDocument(_storage.filesystem("server", "archivo.pdf").output())

Por defecto, el documento se crea con el formato de página A4.

Formato de página​

Para personalizar el formato de la página basta con indicarlo al crear el documento.

Crea un nuevo documento para la salida de datos del servicio con páginas en formato carta:

const documentoPdf = _pdf.newDocument(_pdf.pageSize("letter"))

Crea un nuevo documento para un archivo en la raíz de la aplicación con el formato de página B3:

const documentoPdf = _pdf.newDocument(_app.file("archivo.pdf").output(), _pdf.pageSize("B3"))

Crea un nuevo documento en el storage de la aplicación con el formato de página legal:

const documentoPdf = _pdf.newDocument(_storage.filesystem("server", "archivo.pdf").output(), _pdf.pageSize("legal"))

Consulte la documentación sobre la definición del tamaño de las páginas.

Los documentos PDF se pueden crear con una definición personalizada del tamaño de página; para ello, utilice pageSize pasando el valor del ancho y la altura de la página, por ejemplo:

_pdf.pageSize(50, 30)

Cabecera HTTP​

Para crear un servicio en la API REST que devuelva un archivo PDF como salida de datos, es necesario añadir en la cabecera el parámetro Content-Type del protocolo HTTP.

Ejemplo de cómo definir el contenido como PDF:

_header.contentType("pdf")

El código pdf es una abreviatura de:

_header.contentType("application/pdf")

Salto de página​

Para crear una nueva página se utiliza areaBreak:

documentoPdf.add(
_pdf.areaBreak()
)

Párrafo​

Añade un nuevo párrafo al documento:

documentoPdf.add(
_pdf.paragraph("Mi texto aquí...")
.setFontSize(15)
)

Para añadir un párrafo en una posición fija:

documentoPdf.add(
documentoPdf.add(
_pdf.paragraph("Mi texto fijo...")
.setFixedPosition(100, 500, 200)
.setFontSize(15)
)
)

En el código anterior, en el método setFixedPosition, el primer parámetro es la posición X (100), el segundo parámetro es la posición Y (500) y el tercer parámetro es la anchura (200).

Atención: si el valor de Y se define como 0, significa que queda en el fondo de la página; por tanto, para subirlo hay que aumentar el valor de Y. Cuanto mayor sea el valor de Y, más arriba aparecerá, pero si es demasiado alto y supera la longitud de la página, no será visible.

Documentación con todos los métodos del objeto de Párrafo de iText.

Fuente​

Para definir una fuente nativa podemos hacerlo de la siguiente forma:

const helvetica = _pdf.font("helvetica")
const helveticaNegrita = _pdf.font("helvetica-bold")
const helveticaNegritaOblicua = _pdf.font("helvetica-boldoblique")
const helveticaOblicua = _pdf.font("helvetica-oblique")

Para definir una fuente personalizada a medida, podemos utilizar storage:

const viksiScript = _pdf.font(_storage.filesystem("server", "samples/export-pdf", "viksi-script.ttf"), true)

También podemos utilizar un archivo dentro de la aplicación, como demuestra este ejemplo, en el que la fuente está dentro de la carpeta fonts en la raíz de la aplicación:

const roboto = _pdf.font(_app.file("fonts/roboto.ttf"), true)

O incluso utilizar un archivo en cualquier ruta del sistema; en este ejemplo, la fuente está dentro de la carpeta fonts, que está dentro de la carpeta de archivos temporales del sistema:

const roboto = _pdf.font(_os.file("/tmp/fonts/roboto.ttf"), true)

Documentación con todos los métodos del objeto de Fuente de iText.

Para utilizar la fuente en los textos, se debe pasar la constante de la fuente al párrafo, de esta forma:

documentoPdf.add(
_pdf.paragraph("Utilizando la fuente en negrita.")
.setFont(helveticaNegrita)
.setFontSize(15)
)

Imagen​

La inserción de imágenes en el documento PDF generado programáticamente se hace de esta forma:

documentoPdf.add(
_pdf.image(_storage.filesystem("server", "samples/export-pdf", "logo.png"))
.scaleAbsolute(120, 36)
)

Tenga en cuenta que el método scaleAbsolute es muy útil para redimensionar y ajustar el tamaño de la imagen.

Como alternativa, podemos utilizar un archivo dentro de la aplicación, como en este ejemplo:

const imagenLogo = _pdf.image(_app.file("public/images/logo.png")

O incluso utilizar un archivo en cualquier ruta del sistema, como en este ejemplo:

const imagenLogo = _pdf.image(_os.file("/tmp/images/pdf-logo.png")

Para añadir una imagen en una posición fija:

documentoPdf.add(
imagenLogo
.scaleAbsolute(120, 36)
.setFixedPosition(100, 500)
)

En el código anterior, en el método setFixedPosition, el primer parámetro es la posición X (100) y el segundo parámetro es la posición Y (500).

Atención: si el valor de Y se define como 0, significa que queda en el fondo de la página; por tanto, para subirlo hay que aumentar el valor de Y. Cuanto mayor sea el valor de Y, más arriba aparecerá, pero si es demasiado alto y supera la longitud de la página, no será visible.

Documentación con todos los métodos del objeto de Imagen de iText.

Tabla​

Es posible crear tablas, lo que permite presentar un conjunto de datos de forma más legible.

El número de columnas de la tabla se define con el parámetro: _pdf.table(numeroColumnas)

En el ejemplo siguiente, numeroColumnas es el número 1, es decir, una columna.

Como añadimos dos celdas a la tabla, la segunda celda pasa a la línea siguiente.

Observe:

documentoPdf.add(
_pdf.table(1)
.addCell(
_pdf.cell()
.add(
_pdf.paragraph("Persona")
.setFont(helvetica)
.setFontSize(10)
)
.setBorder(_pdf.border("solid", 2))
.setBackgroundColor(_pdf.colorRGB(1, 0, 0))
)
.addCell(
_pdf.cell()
.add(
_pdf.paragraph("Nicole")
.setFont(helvetica)
.setFontSize(10)
)
.setBackgroundColor(_pdf.colorGray(0.5))
)
)

Con el método addCell se permite insertar la definición de las celdas con su respectivo contenido.

Documentación con todos los métodos del objeto de Celda de iText.

Cerrar y guardar el documento​

El documento solo se guarda o genera por completo cuando se llama al método close, de la siguiente forma:

documentoPdf.close()

El lugar donde se genera el documento se define en la inicialización; por tanto, es en la inicialización del documento donde definimos dónde se generará el documento final, si será en la salida de datos del servicio (output) o en un archivo.

Editar archivo​

Es posible editar un documento PDF ya existente, por ejemplo:

const documentoPdf = _pdf.openDocument(_app.file("archivo.pdf"))

El documento final generado se enviará en la salida de datos del servicio.

Es muy útil para utilizar un PDF como base, como si fuera una plantilla.

Ejemplo para editar un documento en el storage de la aplicación:

const documentoPdf = _pdf.openDocument(_storage.filesystem('server', 'archivo.pdf'))

Utiliza un documento como base, pero guarda los cambios en un nuevo archivo PDF:

const documentoPdf = _pdf.openDocument(
_app.file("documento-base.pdf"),
_app.file("documento-final.pdf")
)

Ejemplo usando como base un documento en el storage, pero lo guarda en otro:

const documentoPdf = _pdf.openDocument(
_storage.filesystem("server", "contrato-base.pdf"),
_storage.filesystem("server", "contrato-final.pdf")
)

Con el documento cargado podemos añadir contenidos y, al cerrarlo con el método close al final, se genera el documento final con el nuevo contenido añadido programáticamente.

Ejemplo de servicio que utiliza un PDF existente como plantilla para generar una salida de archivo PDF con contenido dinámico añadido:

_header.contentType("pdf")

const documentoPdf = _pdf.openDocument(_app.file("plantilla-1.pdf"))

documentoPdf.add(
_pdf.paragraph("Mi contenido dinámico.")
.setFixedPosition(37, 730, 100)
.setFontSize(15)
)

documentoPdf.close()

Leer archivo​

Para leer un documento PDF, extrayendo la información de texto.

Utilizamos el método extract, que obtiene todos los datos del archivo PDF en un objeto JSON.

const datosPdf = _pdf.extract(_app.file("archivo-1.pdf"))

_out.json(datosPdf)

El JSON devuelto tiene la clave content con todo el contenido de texto del archivo PDF, y tiene la clave metadata con las distintas propiedades del archivo PDF.

Aquí tiene un ejemplo del JSON devuelto por extract:

{
"content": "Todo el contenido en texto del archivo PDF viene aquí...",
"metadata":{
"Content-Type":"application/pdf",
"dc:format":"application/pdf; version=1.4",
"dc:title":"Linux 1",
"pdf:PDFVersion":"1.4",
"pdf:charsPerPage":"580",
"pdf:containsDamagedFont":"false",
"pdf:containsNonEmbeddedFont":"false",
"pdf:docinfo:producer":"Skia/PDF m125 Google Docs Renderer",
"pdf:docinfo:title":"Archivo 1",
"pdf:encrypted":"false",
"xmpTPg:NPages":"3",
...
}
}

Existen muchos otros datos que se obtienen en metadata, pero que se han omitido en los ... para que el ejemplo no sea demasiado largo.

Convertir​

Es posible convertir programáticamente el PDF a texto o incluso a HTML.

PDF a texto​

Podemos extraer todo el contenido del archivo PDF en texto corrido utilizando el método:

_pdf.toText(...)

Ejemplo de cómo utilizarlo para leer un archivo en la raíz de la aplicación:

_out.print(_pdf.toText(_app.file("mi-documento.pdf")))

O incluso utilizando un archivo en el storage de la aplicación:

_out.print(_pdf.toText(_storage.filesystem("server", "mi-documento.pdf")))

PDF a HTML​

Podemos extraer todo el contenido del archivo PDF en HTML utilizando el método:

_pdf.toHTML(...)

Ejemplo de cómo utilizarlo para leer un archivo en la raíz de la aplicación:

_out.print(_pdf.toHTML(_app.file("mi-documento.pdf")))

O incluso utilizando un archivo en el storage de la aplicación:

_out.print(_pdf.toHTML(_storage.filesystem("server", "mi-documento.pdf")))

HTML a PDF​

Es posible generar archivos PDF a partir de código HTML utilizando el método:

_pdf.fromHTML(...)

Ejemplo de cómo utilizarlo para generar un archivo de descarga en el servicio:

const icono = _app.file("public/images/icon.png");

const archivoPdf = _pdf.fromHTML(`
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Limelight&family=Roboto" rel="stylesheet">
<style>
* {
font-family: Roboto;
}
div.barra-lateral {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: 100px;
background: #1B5886;
}
div.barra-lateral > div {
position: absolute;
top: 350px;
transform: rotate(-90deg) translate(-50%, -34px);
font-family: Limelight;
font-size: 40px;
color: #89CFF1;
}
main {
padding-left: 150px;
}
h1 {
margin: 0 auto;
text-align: center;
font-family: Limelight;
color: #BA250E;
}
table {
margin: 0 auto;
border: 1px solid black;
border-spacing: 0;
border-collapse: collapse;
}
table > thead {
background: #40712A;
color: #fff;
}
table td, table th {
padding: 5px;
text-align: center;
}
table > tbody > tr:nth-child(even) {
background: #e1e1e1;
}
a {
font-family: Limelight;
font-size: 16px;
font-weight: bold;
text-decoration: none;
color: #1E5FB5;
}
</style>
</head>
<body>
<div class="barra-lateral">
<div>Demostración</div>
</div>
<main>
<h1>PDF a partir del HTML</h1>
<div style="text-align: center; margin: 30px 0;">
<img width="50" src="${icon.uri()}">
</div>
<p>
Este es un HTML de ejemplo para la generación de un archivo PDF.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam
tellus arcu, pharetra quis condimentum eget, dapibus convallis metus.
</p>
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Edad</th>
</tr>
</thread>
<tbody>
<tr>
<td>María Luisa</td>
<td>23</td>
</tr>
<tr>
<td>José Matos</td>
<td>38</td>
</tr>
</tbody>
</table>
<hr style="margin-top: 50px; height: 1px; background: #50B6C6; border: none;"/>
<div>
<a href="https://netuno.org" target="_blank">Netuno.org</a>
</div>
</main>
</body>
</html>
`);

_header.contentTypePDF();
_out.copy(archivoPdf.input());

Vídeos​

A continuación se muestran algunos vídeos de ejemplo sobre cómo realizar la generación de archivos PDF programáticamente: