Sari la conținutul principal

PDF

Procesarea fișierelor PDF personalizate.

Introducere​

Netuno generează fișiere PDF cu suport pentru imagini, tabele, fonturi și multe altele, prin resursa PDF care permite o abstractizare low-code a iTextPDF pentru diversele limbaje de programare acceptate de Netuno.

Aplicația care vine împreună cu Netuno, denumită demo (aplicație de demonstrație), conține numeroase exemple de implementări de cod în diverse limbaje, inclusiv demonstrația exportului în PDF prin serviciul export-pdf, care se află în:

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

Inițializare​

În primul rând, este necesar să se creeze obiectul care va conține contextul noului document ce este creat; în mod implicit, dimensiunea paginilor este A4.

La crearea documentului se definește unde va fi generat.

Dacă nu se definește nimic, acesta va fi generat în ieșirea de date a serviciului, de exemplu:

const documentPdf = _pdf.newDocument()

Pentru a genera un fișier PDF în folderul docs al aplicației, se poate face astfel:

const documentPdf = _pdf.newDocument(_app.file("docs/fișier.pdf").output())

Dacă se generează documentul într-un fișier nou în afara aplicației, de exemplu:

const documentPdf = _pdf.newDocument(_os.file("/tmp/fișier.pdf").output())

Sau chiar în storage-ul aplicației:

const documentPdf = _pdf.newDocument(_storage.filesystem("server", "fișier.pdf").output())

În mod implicit, documentul este creat cu formatul de pagină A4.

Formatul paginii​

Pentru a personaliza formatul paginii, este suficient să se indice la crearea documentului.

Creează un document nou pentru ieșirea de date a serviciului cu pagini în formatul letter:

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

Creează un document nou pentru un fișier în rădăcina aplicației cu formatul de pagină B3:

const documentPdf = _pdf.newDocument(_app.file("fișier.pdf").output(), _pdf.pageSize("B3"))

Creează un document nou în storage-ul aplicației cu formatul de pagină legal:

const documentPdf = _pdf.newDocument(_storage.filesystem("server", "fișier.pdf").output(), _pdf.pageSize("legal"))

Consultați documentația despre definirea dimensiunii paginilor.

Documentele PDF pot fi create cu definirea dimensiunii personalizate a paginii; pentru aceasta, utilizați pageSize transmițând valorile lățimii și înălțimii paginii, de exemplu:

_pdf.pageSize(50, 30)

Antet HTTP​

Pentru a crea un serviciu în API-ul REST care returnează un fișier PDF ca ieșire de date, este necesar să se adauge în antet parametrul Content-Type al protocolului HTTP.

Exemplu de definire a conținutului ca PDF:

_header.contentType("pdf")

Codul pdf este o abreviere pentru:

_header.contentType("application/pdf")

Salt de pagină​

Pentru a crea o pagină nouă se utilizează areaBreak:

documentPdf.add(
_pdf.areaBreak()
)

Paragraf​

Adaugă un paragraf nou în document:

documentPdf.add(
_pdf.paragraph("Textul meu aici...")
.setFontSize(15)
)

Pentru a adăuga un paragraf într-o poziție fixă:

documentPdf.add(
documentPdf.add(
_pdf.paragraph("Textul meu fix...")
.setFixedPosition(100, 500, 200)
.setFontSize(15)
)
)

În codul de mai sus, în metoda setFixedPosition, primul parametru este poziția X (100), al doilea parametru este poziția Y (500), iar al treilea parametru este lățimea (200).

Atenție: dacă Y este definit cu valoarea 0, înseamnă că se află în partea de jos a paginii; prin urmare, pentru a urca trebuie crescută valoarea lui Y; cu cât valoarea lui Y este mai mare, cu atât va apărea mai sus, dar dacă este prea mare dincolo de lungimea paginii, atunci nu va fi vizibil.

Documentație cu toate metodele obiectului Paragraf din iText.

Font​

Pentru a defini un font nativ, putem face astfel:

const helvetica = _pdf.font("helvetica")
const helveticaAldin = _pdf.font("helvetica-bold")
const helveticaAldinOblique = _pdf.font("helvetica-boldoblique")
const helveticaOblique = _pdf.font("helvetica-oblique")

Iar pentru a defini un font personalizat la cerere, putem utiliza storage:

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

De asemenea, putem utiliza un fișier din interiorul aplicației, cum demonstrează acest exemplu, în care fontul se află în folderul fonturi din rădăcina aplicației:

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

Sau chiar un fișier aflat în orice cale a sistemului; astfel, în acest exemplu fontul se află în folderul fonturi care este în folderul de fișiere temporare al sistemului:

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

Documentație cu toate metodele obiectului Font din iText.

Pentru a utiliza fontul în texte, trebuie transmisă constanta fontului către paragraf, astfel:

documentPdf.add(
_pdf.paragraph("Folosind fontul îngroșat.")
.setFont(helveticaAldin)
.setFontSize(15)
)

Imagine​

Inserarea imaginilor în documentul PDF generat programatic se face astfel:

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

Observați că metoda scaleAbsolute este foarte utilă pentru redimensionarea și ajustarea dimensiunii imaginii.

Ca alternativă, putem utiliza un fișier din interiorul aplicației, ca în acest exemplu:

const imagineLogo = _pdf.image(_app.file("public/imagini/logo.png")

Sau chiar un fișier aflat în orice cale a sistemului, ca în acest exemplu:

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

Pentru a adăuga o imagine într-o poziție fixă:

documentPdf.add(
imagineLogo
.scaleAbsolute(120, 36)
.setFixedPosition(100, 500)
)

În codul de mai sus, în metoda setFixedPosition, primul parametru este poziția X (100), iar al doilea parametru este poziția Y (500).

Atenție: dacă Y este definit cu valoarea 0, înseamnă că se află în partea de jos a paginii; prin urmare, pentru a urca trebuie crescută valoarea lui Y; cu cât valoarea lui Y este mai mare, cu atât va apărea mai sus, dar dacă este prea mare dincolo de lungimea paginii, atunci nu va fi vizibil.

Documentație cu toate metodele obiectului Imagine din iText.

Tabel​

Este posibil să se creeze tabele, ceea ce permite prezentarea unui set de date într-o formă mai lizibilă.

Numărul de coloane al tabelului este definit cu parametrul: _pdf.table(numarColoane)

În exemplul de mai jos, numarColoane este numărul 1, adică o coloană.

Deoarece adăugăm două celule în tabel, a doua celulă trece pe linia următoare.

Observați:

documentPdf.add(
_pdf.table(1)
.addCell(
_pdf.cell()
.add(
_pdf.paragraph("Persoană")
.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))
)
)

Metoda addCell permite inserirea definiției celulelor împreună cu conținutul lor respectiv.

Documentație cu toate metodele obiectului Celulă din iText.

Închiderea și salvarea documentului​

Documentul este salvat sau generat complet doar atunci când este apelată metoda close, astfel:

documentPdf.close()

Locul unde este generat documentul este definit la inițializare; prin urmare, la inițializarea documentului definim unde va fi generat documentul final, fie în ieșirea de date a serviciului (output), fie într-un fișier.

Editarea fișierului​

Este posibil să editați un document PDF deja existent, de exemplu:

const documentPdf = _pdf.openDocument(_app.file("fișier.pdf"))

Documentul final generat va fi trimis în ieșirea de date a serviciului.

Este foarte util pentru a utiliza un PDF ca bază, ca și cum ar fi un șablon.

Exemplu pentru editarea unui document în storage-ul aplicației:

const documentPdf = _pdf.openDocument(_storage.filesystem('server', 'fișier.pdf'))

Utilizează un document ca bază, dar salvează modificările într-un fișier PDF nou:

const documentPdf = _pdf.openDocument(
_app.file("document-baza.pdf"),
_app.file("document-final.pdf")
)

Exemplu care utilizează ca bază un document din storage, dar salvează într-un altul:

const documentPdf = _pdf.openDocument(
_storage.filesystem("server", "contract-baza.pdf"),
_storage.filesystem("server", "contract-final.pdf")
)

Cu documentul încărcat, putem adăuga conținuturi, iar la închiderea cu metoda close la final este generat documentul final cu noul conținut adăugat programatic.

Exemplu de serviciu care utilizează un PDF existent ca șablon pentru a genera o ieșire de fișier PDF cu conținut dinamic adăugat:

_header.contentType("pdf")

const documentPdf = _pdf.openDocument(_app.file("sablon-1.pdf"))

documentPdf.add(
_pdf.paragraph("Conținutul meu dinamic.")
.setFixedPosition(37, 730, 100)
.setFontSize(15)
)

documentPdf.close()

Citirea fișierului​

Pentru a citi un document PDF, extrăgând informațiile text.

Utilizăm metoda extract, care obține toate datele fișierului PDF într-un obiect JSON.

const datePdf = _pdf.extract(_app.file("fișier-1.pdf"))

_out.json(datePdf)

JSON-ul returnat are cheia content cu tot conținutul text al fișierului PDF și are cheia metadata cu diversele proprietăți ale fișierului PDF.

Iată un exemplu de JSON returnat de extract:

{
"content": "Tot conținutul text al fișierului PDF apare aici...",
"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":"Fișier 1",
"pdf:encrypted":"false",
"xmpTPg:NPages":"3",
...
}
}

Există multe alte date obținute în metadata, dar care au fost omise în ... pentru ca exemplul să nu devină prea lung.

Conversie​

Este posibil să convertiți programatic PDF-ul în text sau chiar în HTML.

PDF în text​

Putem extrage tot conținutul fișierului PDF în text continuu utilizând metoda:

_pdf.toText(...)

Exemplu de utilizare pentru a citi un fișier din rădăcina aplicației:

_out.print(_pdf.toText(_app.file("documentul-meu.pdf")))

Sau chiar utilizând un fișier din storage-ul aplicației:

_out.print(_pdf.toText(_storage.filesystem("server", "documentul-meu.pdf")))

PDF în HTML​

Putem extrage tot conținutul fișierului PDF în HTML utilizând metoda:

_pdf.toHTML(...)

Exemplu de utilizare pentru a citi un fișier din rădăcina aplicației:

_out.print(_pdf.toHTML(_app.file("documentul-meu.pdf")))

Sau chiar utilizând un fișier din storage-ul aplicației:

_out.print(_pdf.toHTML(_storage.filesystem("server", "documentul-meu.pdf")))

HTML în PDF​

Este posibil să generați fișiere PDF pornind de la cod HTML, utilizând metoda:

_pdf.fromHTML(...)

Exemplu de utilizare pentru a genera un fișier de descărcare în serviciu:

const pictograma = _app.file("public/imagini/pictograma.png");

const fisierPdf = _pdf.fromHTML(`
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Limelight&family=Roboto" rel="stylesheet">
<style>
* {
font-family: Roboto;
}
div.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: 100px;
background: #1B5886;
}
div.sidebar > 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="sidebar">
<div>Demonstrație</div>
</div>
<main>
<h1>PDF din HTML</h1>
<div style="text-align: center; margin: 30px 0;">
<img width="50" src="${icon.uri()}">
</div>
<p>
Acesta este un HTML de exemplu pentru generarea unui fișier PDF.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam
tellus arcu, pharetra quis condimentum eget, dapibus convallis metus.
</p>
<table>
<thead>
<tr>
<th>Nume</th>
<th>Vârstă</th>
</tr>
</thread>
<tbody>
<tr>
<td>Maria Luísa</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(fisierPdf.input());

Videoclipuri​

Urmează câteva videoclipuri exemplificative despre cum se realizează generarea fișierelor PDF programatic: