Articles

Genererer PDF fra HTML Med Node.js og Nå

Máté Boér Bilde's Picture

Máté Boér

Full-Stack-Utvikler i RisingStack

I denne artikkelen skal jeg vise hvordan du kan generere PDF-dokument fra et tungt stylet Reagere side ved hjelp av Noden.js, Dukkespiller, hodeløs Krom & Docker.

Bakgrunn: For noen måneder siden ba En Av Klientene Til RisingStack oss om å utvikle en funksjon der brukeren kunne be Om En React-side I PDF-format. Denne siden er i utgangspunktet en rapport / resultat for pasienter med datavisualisering, som inneholder mange Svg-er. Videre var det noen spesielle forespørsler om å manipulere oppsettet og gjøre noen omarrangementer AV HTML-elementene. SÅ PDF-FILEN skal ha forskjellig styling og tillegg i forhold til den opprinnelige React-siden.

da oppdraget var litt mer komplekst enn det som kunne vært løst med enkle CSS-regler, utforsket vi først mulige implementeringer. I hovedsak fant vi 3 hovedløsninger. Denne bloggposten vil lede deg gjennom disse mulighetene og de endelige implementeringene.

en personlig kommentar før vi kommer i gang: det er litt av en problemfri,så spenne opp!

Innholdsfortegnelse:

  • Klient side eller Backend side?
  • Alternativ 1: Lag Et Skjermbilde FRA DOM
  • Alternativ 2: Bruk BARE ET PDF-bibliotek
  • Endelig alternativ 3: Puppeteer, headless Chrome med Node.Js
    • stil manipulasjon
    • Send fil til klienten Og lagre den
  • Bruke Puppeteer Med Docker
  • alternativ 3 +1: CSS utskriftsregler
  • Sammendrag

Klientsiden eller Serversiden?

DET er mulig å generere EN PDF-fil både på klientsiden og på serversiden. Det er imidlertid sannsynligvis mer fornuftig å la backend håndtere det, da du ikke vil bruke opp alle ressursene brukerens nettleser kan tilby.

Likevel vil Jeg fortsatt vise løsninger for begge metodene.

Alternativ 1: Lag Et Skjermbilde FRA DOM

ved første øyekast syntes denne løsningen å være den enkleste, og det viste seg å være sant, men det har sine egne begrensninger. Hvis du ikke har spesielle behov, som valgbar eller søkbar tekst I PDF, er det en god og enkel måte å generere en.

denne metoden er enkel: lag et skjermbilde fra siden, og legg det i EN PDF-fil. Ganske grei. Vi brukte to pakker for denne tilnærmingen: Html2canvas, for å lage et skjermbilde FRA DOM jsPdf, et bibliotek for å generere PDF

La oss starte koding.

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')})

Og det er det!

Pass på at du tar en titt påhtml2canvasonclone – metoden. Det kan vise seg å være nyttig når du raskt trenger å ta et øyeblikksbilde og manipulere DOM (f. eks skjule utskriftsknappen) før du tar bildet. Jeg kan se ganske mange brukstilfeller for denne pakken. Dessverre var vår ikke en, da vi trengte Å håndtere PDF-opprettelsen på baksiden.

Alternativ 2: Bruk BARE ET PDF-Bibliotek

Det finnes flere biblioteker der ute på NPM for dette formålet, som jsPDF (nevnt ovenfor) eller PDFKit. Problemet med dem at jeg måtte gjenskape sidestrukturen igjen hvis jeg ønsket å bruke disse bibliotekene. Det gjør definitivt vondt vedlikehold, da jeg måtte ha brukt alle påfølgende endringer i BÅDE PDF-malen og React-siden.

Ta en titt på koden nedenfor. DU må lage PDF-dokumentet selv for hånd. Nå kan du krysse DOM og finne ut hvordan du oversetter hvert element TIL PDF, men det er en kjedelig jobb. Det må være en enklere måte.

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()

denne kodebiten er Fra PDFKit-dokumentene. Det kan imidlertid være nyttig hvis målet ditt er EN PDF-fil med en GANG og ikke konverteringen av en allerede eksisterende (OG stadig skiftende) HTML-side.

Siste Alternativ 3: Puppeteer, Hodeløs Krom Med Node.js

Hva Er Puppeteer? Dokumentasjonen sier:

Puppeteer Er Et Nodebibliotek som gir ET API PÅ HØYT NIVÅ for å kontrollere Chrome eller Chromium over DevTools-Protokollen. Puppeteer kjører headless som standard, men kan konfigureres til å kjøre full (ikke-headless) Chrome eller Chromium.

det er i utgangspunktet en nettleser som du kan kjøre Fra Node.js. Hvis du leser dokumentene, er det første som står om Puppeteer at du kan Bruke den Til Å Generere skjermbilder og Pdf-Filer av sider. Utmerket! Det var det vi lette etter.

la oss installere Puppeteer med npmi i puppeteer, og implementere vår brukstilfelle.

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})

dette er en enkel funksjon som navigerer til EN URL og genererer EN PDF-fil av nettstedet.Først starter vi nettleseren (PDF-generasjon støttes bare i hodeløs modus), så åpner vi en ny side, setter visningsporten og navigerer til den angitte NETTADRESSEN.

Innstilling av waitUntil: ‘networkidle0’ betyr At Puppeteer anser navigasjonen for å være ferdig når det ikke er noen nettverkstilkoblinger i minst 500 ms.)

etter det lagrer VI PDF-filen til en variabel, vi lukker nettleseren og returnerer PDF-FILEN.

Merk: page.pdf – metoden mottar et options – objekt, der du også kan lagre filen på disk med «path» – alternativet. HVIS banen ikke er angitt, PDF vil ikke bli lagret på disken, får du en buffer i stedet. Senere diskuterer jeg hvordan du kan håndtere det.)

hvis du må logge inn først for å generere EN PDF fra en beskyttet side, må du først navigere til påloggingssiden, inspisere skjemaelementene FOR ID eller navn, fylle dem inn og sende inn skjemaet:

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

lagre alltid påloggingsinformasjon i miljøvariabler, ikke hardcode dem!

Stil Manipulasjon

Puppeteer har en løsning for denne stilen manipulasjon også. Du kan sette inn stilkoder før du genererer PDF-FILEN, og Puppeteer vil generere en fil med de endrede stilene.

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

Send fil til klienten og lagre den

Ok, nå har du generert EN PDF-fil på backend. Hva skal jeg gjøre nå?

som jeg nevnte ovenfor, hvis du ikke lagrer filen på disk, får du en buffer. Du trenger bare å sende den bufferen med riktig innholdstype til frontenden.

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

Nå kan du bare sende en forespørsel til serveren, for å få den genererte PDF.

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

når du har sendt forespørselen, bør bufferen begynne å laste ned. Nå er det siste trinnet å konvertere bufferen til EN PDF-fil.

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>

Det var det! Hvis du klikker på lagre-knappen, BLIR PDF-FILEN lagret av nettleseren.

Bruke Puppeteer Med Docker

jeg tror dette er den vanskeligste delen av implementeringen – så la meg spare deg for Et par timer Med Googling.den offisielle dokumentasjonen sier at «å få headless Chrome oppe og går I Docker kan være vanskelig». De offisielle dokumentene har En Feilsøkingsdel, hvor du i skrivende stund finner all nødvendig informasjon om installering av puppeteer Med Docker.

hvis Du installerer Puppeteer på Alpine-bildet, må du rulle litt ned til denne delen av siden. Ellers kan du glatte over det faktum at du ikke kan kjøre den nyeste Puppeteer-versjonen, og du må også deaktivere shm-bruk, ved hjelp av et flagg:

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

Ellers Kan Puppeteer-underprosessen gå tom for minne før den selv blir startet riktig. Mer info om det på feilsøkingslenken ovenfor.

Alternativ 3 + 1: CSS Print Rules

man skulle tro at BARE å bruke CSS print regler er lett fra en utviklere ståsted. Ingen npm moduler, bare ren CSS. Men hvordan de fare når det gjelder cross-browser kompatibilitet?Når DU velger CSS-utskriftsregler, må du teste utfallet i hver nettleser for å sikre at det gir samme layout, og det er ikke 100% at det gjør det.for eksempel kan det å sette inn en pause etter et gitt element ikke betraktes som en esoterisk brukstilfelle, men du kan bli overrasket over at du må bruke løsninger for Å få det til Å fungere I Firefox.

Med mindre du er en kamp-herdet CSS magiker med mye erfaring i å lage utskrivbare sider, kan dette være tidkrevende.

Utskriftsregler er gode hvis du kan holde utskriftsstilarkene enkle.

La oss se et eksempel.

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

DENNE CSS ovenfor skjuler utskriftsknappen, og setter inn en sideskift etter hverdiv med klassencontent. det er en flott artikkel som oppsummerer hva du kan gjøre med utskriftsregler, og hva er vanskelighetene med dem, inkludert nettleserkompatibilitet.

MED alt i betraktning ER CSS-utskriftsregler flotte og effektive hvis DU vil lage EN PDF fra en ikke så kompleks side.

Sammendrag: PDF FRA HTML Med Node.js og Puppeteer

så la oss raskt gå gjennom alternativene vi dekket her for å generere PDF-filer fra HTML-sider:

  • Skjermbilde FRA DOM: Dette kan være nyttig når du trenger å lage øyeblikksbilder fra en side (for eksempel for å lage et miniatyrbilde), men kommer til kort når du har mye data å håndtere.
  • Bruk BARE ET PDF-bibliotek: hvis DU trenger Å lage PDF-filer programmatisk fra bunnen av, er dette en perfekt løsning. Ellers må du opprettholde HTML og PDF-maler som er definitivt en no-go.
  • Dukketeater: Til tross for at det var relativt vanskelig å få Det til Å fungere På Docker, ga det det beste resultatet for vår brukstilfelle, og det var også det enkleste å skrive koden med.CSS utskriftsregler: HVIS brukerne er utdannet nok til å vite hvordan du skriver ut til en fil og sidene dine er relativt enkle, kan det være den mest smertefrie løsningen. Som du så i vårt tilfelle, var det ikke.

Glad utskrift!

Relaterte emner

Node.js Tutorials For Nybegynnere / @ RisingStack