Articles

generarea PDF din HTML cu nod.js și Păpușar

Máté Boér Imagine's Picture

Máté Boér

Full-Stack Developer la RisingStack

În acest articol vă voi arăta cum puteți genera un document PDF dintr-un puternic stil de a Reacționa pagină folosind Nod.js, păpușar, Chrome fără cap & Docker.

fundal: În urmă cu câteva luni, unul dintre clienții RisingStack ne-a cerut să dezvoltăm o caracteristică în care utilizatorul să poată solicita o pagină React în format PDF. Această pagină este în esență un raport / rezultat pentru pacienții cu vizualizare a datelor, care conține o mulțime de SVG-uri. În plus, au existat unele cereri speciale pentru a manipula aspectul și de a face unele rearanjamente ale elementelor HTML. Deci, PDF-ul ar trebui să aibă un stil și completări diferite în comparație cu pagina React originală.

deoarece atribuirea a fost un pic mai complexă decât ceea ce ar fi putut fi rezolvată cu reguli CSS simple, am explorat mai întâi posibile implementări. În esență, am găsit 3 soluții principale. Acest blogpost vă va ghida prin aceste posibilități și implementările finale.

un comentariu personal înainte de a începe: este destul de un hassle, așa cataramă!

cuprins:

  • partea clientului sau partea Backend?
  • opțiunea 1: Realizarea unei capturi de ecran din DOM
  • Opțiunea 2: Utilizați doar o bibliotecă PDF
  • opțiunea finală 3: păpușar, Chrome fără cap cu nod.JS
    • manipulare stil
    • Trimite fișier la client și salvați-l
  • folosind Puppeteer cu Docker
  • Opțiunea 3 +1: Reguli de imprimare CSS
  • rezumat

partea Client sau Server?

este posibil să generați un fișier PDF atât pe partea clientului, cât și pe partea serverului. Cu toate acestea, probabil că are mai mult sens să lăsați backend-ul să se ocupe de el, deoarece nu doriți să utilizați toate resursele pe care le poate oferi browserul utilizatorului.

chiar și așa, voi arăta în continuare soluții pentru ambele metode.

opțiunea 1: Faceți o captură de ecran din DOM

la prima vedere, această soluție părea a fi cea mai simplă și sa dovedit a fi adevărată, dar are propriile limitări. Dacă nu aveți nevoi speciale, cum ar fi textul selectabil sau căutabil în PDF, este o modalitate bună și simplă de a genera unul.

această metodă este simplă și simplă: Creați o captură de ecran din pagină și puneți-o într-un fișier PDF. Destul de simplu. Am folosit două pachete pentru această abordare:

Html2canvas, pentru a face o captură de ecran din DOM
jsPdf, o bibliotecă pentru a genera PDF

să începem codificarea.

npm install html2canvas jspdf

import html2canvas from 'html2canvas'import jsPdf from 'jspdf' function printPDF () { const domElement = document.getElementById('your-id') html2canvas(domElement, { onclone: (document) => { document.getElementById('print-button').style.visibility = 'hidden'}}) .then((canvas) => { const img = canvas.toDataURL('image/png') const pdf = new jsPdf() pdf.addImage(imgData, 'JPEG', 0, 0, width, height) pdf.save('your-filename.pdf')})

și asta este!

asigurați-vă că aruncați o privire la metodahtml2canvasonclone. Se poate dovedi a fi la îndemână atunci când aveți nevoie rapid pentru a lua un instantaneu și manipula DOM (de exemplu, ascunde butonul de imprimare) înainte de a lua imaginea. Pot vedea destul de multe cazuri de utilizare pentru acest pachet. Din păcate, al nostru nu a fost unul, deoarece trebuia să gestionăm crearea PDF pe partea de backend.

Opțiunea 2: Utilizați numai o bibliotecă PDF

există mai multe biblioteci acolo pe NPM în acest scop, cum ar fi jsPDF (menționat mai sus) sau PDFKit. Problema cu ei că ar trebui să recreez din nou structura paginii dacă aș vrea să folosesc aceste biblioteci. Acest lucru dăunează cu siguranță mentenabilității, deoarece ar fi trebuit să aplic toate modificările ulterioare atât șablonului PDF, cât și paginii React.

uitați-vă la codul de mai jos. Trebuie să creați singur documentul PDF. Acum ai putea traversa DOM și dau seama cum să traducă fiecare element pentru cele PDF, dar că este un loc de muncă plictisitor. Trebuie să existe o cale mai ușoară.

doc = new PDFDocumentdoc.pipe fs.createWriteStream('output.pdf')doc.font('fonts/PalatinoBold.ttf') .fontSize(25) .text('Some text with an embedded font!', 100, 100) doc.image('path/to/image.png', { fit: , align: 'center', valign: 'center'}); doc.addPage() .fontSize(25) .text('Here is some vector graphics...', 100, 100) doc.end()

Acest Fragment este din documentele PDFKit. Cu toate acestea, poate fi util dacă ținta dvs. este un fișier PDF imediat și nu conversia unei pagini HTML deja existente (și în continuă schimbare).

opțiunea finală 3: păpușar, crom fără cap cu nod.js

Ce este păpușarul? Documentația spune:

Puppeteer este o bibliotecă nod care oferă un API de nivel înalt pentru a controla Chrome sau Chromium peste protocolul DevTools. Puppeteer rulează fără cap în mod implicit, dar poate fi configurat să ruleze crom sau crom complet (fără cap).

este practic un browser pe care îl puteți rula de la nod.js. Dacă citiți documentele, primul lucru pe care îl spune despre păpușar este că îl puteți folosi pentru a genera capturi de ecran și PDF-uri ale paginilor’. Excelent! Asta căutam.

să instalăm păpușarul cunpmi i puppeteer și să implementăm cazul nostru de utilizare.

const puppeteer = require('puppeteer') async function printPDF() { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.goto('https://blog.risingstack.com', {waitUntil: 'networkidle0'}); const pdf = await page.pdf({ format: 'A4' }); await browser.close(); return pdf})

aceasta este o funcție simplă care navighează la o adresă URL și generează un fișier PDF al site-ului.

Mai întâi, lansăm browserul (generarea PDF acceptată doar în modul fără cap), apoi deschidem o nouă pagină, setăm portul de vizualizare și navigăm la adresa URL furnizată.

setarea opțiuniiwaitUntil: ‘networkidle0’ înseamnă că Puppeteer consideră că navigarea este terminată atunci când nu există conexiuni de rețea timp de cel puțin 500 ms. (verificați documentele API pentru informații suplimentare.)

după aceea, salvăm PDF-ul într-o variabilă, închidem browserul și returnăm PDF-ul.

notă: Metodapage.pdfprimește un obiectoptions, unde puteți salva fișierul pe disc și cu opțiunea „cale”. Dacă calea nu este furnizată, PDF-ul nu va fi salvat pe disc, veți primi în schimb un tampon. Mai târziu, voi discuta despre cum te poți descurca.)

În cazul în care trebuie să vă conectați mai întâi pentru a genera un PDF dintr-o pagină protejată, mai întâi trebuie să navigați la pagina de conectare, să inspectați elementele formularului pentru ID sau Nume, să le completați, apoi să trimiteți formularul:

await page.type('#email', process.env.PDF_USER)await page.type('#password', process.env.PDF_PASSWORD)await page.click('#submit')

stoca întotdeauna datele de conectare în variabile de mediu, nu le hardcode!

manipularea stilului

păpușarul are o soluție și pentru această manipulare a stilului. Puteți insera etichete de stil înainte de a genera PDF-ul, iar Puppeteer va genera un fișier cu stilurile modificate.

await page.addStyleTag({ content: '.nav { display: none} .navbar { border: 0px} #print-button {display: none}' })

trimiteți fișierul clientului și salvați-l

bine, acum ați generat un fișier PDF pe backend. Ce să fac acum?după cum am menționat mai sus, dacă nu salvați fișierul pe disc, veți obține un tampon. Trebuie doar să trimiteți acel tampon cu tipul de conținut adecvat la front-end.

printPDF.then(pdf => {res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length })res.send(pdf)

acum Puteți trimite pur și simplu o solicitare către server, pentru a obține PDF-ul generat.

function getPDF() { return axios.get(`${API_URL}/your-pdf-endpoint`, { responseType: 'arraybuffer', headers: { 'Accept': 'application/pdf' } })

după ce ați trimis cererea, tamponul ar trebui să înceapă descărcarea. Acum ultimul pas este să convertiți tamponul într-un fișier PDF.

savePDF = () => { this.openModal(‘Loading…’) // open modal return getPDF() // API call .then((response) => { const blob = new Blob(, {type: 'application/pdf'}) const link = document.createElement('a') link.href = window.URL.createObjectURL(blob) link.download = `your-file-name.pdf` link.click() this.closeModal() // close modal }) .catch(err => /** error handling **/) }

<button onClick={this.savePDF}>Save as PDF</button>

asta a fost! Dacă faceți clic pe butonul Salvare, PDF-ul va fi salvat de browser.

folosind Puppeteer cu Docker

cred că aceasta este cea mai dificilă parte a implementării – așa că permiteți-mi să vă salvez câteva ore de Googling.

documentația oficială afirmă că „instalarea Chrome fără cap și funcționarea în Docker poate fi dificilă”. Documentele oficiale au o secțiune de depanare, unde la momentul scrierii puteți găsi toate informațiile necesare despre instalarea puppeteer cu Docker.

Dacă instalați Puppeteer pe imaginea alpină, asigurați-vă că derulați puțin în jos până la această parte a paginii. În caz contrar, s-ar putea gloss peste faptul că nu puteți rula cea mai recentă versiune Puppeteer și, de asemenea, trebuie să dezactivați utilizarea shm, folosind un steag:

const browser = await puppeteer.launch({ headless: true, args: });

În caz contrar, procesul de sub Puppeteer ar putea alerga afară de memorie înainte de a începe chiar în mod corespunzător. Mai multe informații despre asta pe linkul de depanare de mai sus.

Opțiunea 3 + 1: Reguli de imprimare CSS

s-ar putea crede că simpla utilizare a regulilor de imprimare CSS este ușoară din punctul de vedere al dezvoltatorilor. Nu există module NPM, doar CSS pur. Dar cum se descurcă atunci când vine vorba de compatibilitatea între browsere?

atunci când alegeți regulile de imprimare CSS, trebuie să testați rezultatul în fiecare browser pentru a vă asigura că oferă același aspect și nu este 100% că o face.

de exemplu, inserarea unei pauze după un anumit element nu poate fi considerată un caz de utilizare ezoterică, totuși s-ar putea să fiți surprins că trebuie să utilizați soluții pentru a obține acest lucru în Firefox.

dacă nu sunteți un magician CSS întărit de luptă, cu multă experiență în crearea de pagini imprimabile, acest lucru poate necesita mult timp.

regulile de imprimare sunt excelente dacă puteți păstra foile de stil de imprimare simple.

să vedem un exemplu.

@media print { .print-button { display: none; } .content div { break-after: always; }}

acest CSS de mai sus ascunde butonul de imprimare și introduce o pauză de pagină după fiecarediv cu clasacontent. există un articol excelent care rezumă ce puteți face cu regulile de imprimare și care sunt dificultățile cu acestea, inclusiv compatibilitatea browserului.

ținând cont de toate, regulile de imprimare CSS sunt excelente și eficiente dacă doriți să creați un PDF dintr-o pagină nu atât de complexă.

rezumat: PDF din HTML cu nod.js și Puppeteer

deci, să trecem rapid prin opțiunile pe care le-am acoperit aici pentru generarea fișierelor PDF din paginile HTML:

  • Screenshot din DOM: acest lucru poate fi util atunci când trebuie să creați instantanee dintr-o pagină (de exemplu, pentru a crea o miniatură), dar se încadrează scurt atunci când aveți o mulțime de date de gestionat.
  • folosiți doar o bibliotecă PDF: Dacă trebuie să creați fișiere PDF programatic de la zero, aceasta este o soluție perfectă. În caz contrar, aveți nevoie pentru a menține HTML și PDF template-uri, care este cu siguranta un no-go.
  • păpușar: În ciuda faptului că a fost relativ dificil să-l facem să funcționeze la Docker, a oferit cel mai bun rezultat pentru cazul nostru de utilizare și a fost, de asemenea, cel mai ușor de scris codul.
  • reguli de imprimare CSS: dacă utilizatorii dvs. sunt suficient de educați pentru a ști cum să imprimați într-un fișier și paginile dvs. sunt relativ simple, poate fi soluția cea mai nedureroasă. După cum ați văzut în cazul nostru, nu a fost.

imprimare fericită!

subiecte conexe

nod.JS tutoriale pentru incepatori / @ RisingStack