Articles

Generating PDF from HTML with Node.js ja nukketeatteri

Máté Boérin Kuva's Picture

Máté Boér

Full-Stack Developer at RisingStack

tässä artikkelissa aion näyttää, miten voimakkaasti tyylitellyltä React-sivulta voi luoda PDF-dokumentin solmua käyttäen.js, nukketeatteri, päätön Kromi & Docker.

Tausta: Muutama kuukausi sitten yksi Risingstackin asiakkaista pyysi meitä kehittämään ominaisuuden, jossa käyttäjä voisi pyytää React-sivua PDF-muodossa. Tämä sivu on pohjimmiltaan raportti / tulos potilaille, joilla on tietojen visualisointi, joka sisältää paljon SVGs. Lisäksi, oli joitakin erityisiä pyyntöjä manipuloida ulkoasua ja tehdä joitakin uudelleenjärjestelyjä HTML-elementtejä. PDF: ssä pitäisi siis olla erilainen muotoilu ja lisäykset verrattuna alkuperäiseen React-sivulle.

koska tehtävä oli hieman monimutkaisempi kuin mitä olisi voitu ratkaista yksinkertaisilla CSS-säännöillä, tutkimme ensin mahdollisia toteutuksia. Pohjimmiltaan löysimme 3 tärkeimmät ratkaisut. Tämä blogiposti opastaa näitä mahdollisuuksia ja lopullisia toteutuksia.

henkilökohtainen kommentti ennen kuin aloitamme: aikamoista hässäkkää, joten vyöt kiinni!

Sisällysluettelo:

  • asiakaspuoli vai taustapuoli?
  • vaihtoehto 1: kuvakaappauksen tekeminen Domista
  • Vaihtoehto 2: Käytä vain PDF-kirjastoa
  • lopullinen vaihtoehto 3: nukketeatteri, päätön Kromi solmulla.js
    • Tyylimanipulaatio
    • Lähetä tiedosto asiakkaalle ja tallenna se
  • käyttäen nukketeatteria Dockerin kanssa

  • vaihtoehto 3 +1: CSS-tulostussäännöt
  • Yhteenveto

asiakkaan puolella vai palvelimen puolella?

on mahdollista luoda PDF-tiedosto sekä asiakas-että palvelinpuolelle. On kuitenkin todennäköisesti järkevämpää antaa taustaohjelman hoitaa asia, sillä et halua käyttää kaikkia resursseja, joita käyttäjän selain voi tarjota.

silti näytän ratkaisuja molempiin menetelmiin.

vaihtoehto 1: Tee kuvakaappaus DOM

ensi näkemältä tämä ratkaisu tuntui yksinkertaisimmalta, ja se osoittautui todeksi, mutta sillä on omat rajoituksensa. Jos sinulla ei ole erityistarpeita, kuten valittavissa tai haettavissa olevaa tekstiä PDF, se on hyvä ja yksinkertainen tapa luoda yksi.

tämä menetelmä on yksinkertainen ja yksinkertainen: luo kuvakaappaus sivulta ja laita se PDF-tiedostoon. Aika yksinkertaista. Käytimme tähän lähestymistapaan kahta pakettia:

Html2canvas, tehdäksemme kuvakaappauksen DOM
jsPdf-kirjastosta, joka tuottaa PDF: n

aloitetaan koodaus.

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

ja se siitä!

Katso html2canvasonclone metodi. Se voi osoittautua käteväksi, kun sinun on nopeasti otettava tilannekuva ja manipuloitava DOM-näppäintä (esim.piilotettava print-painike) ennen kuvan ottamista. Näen aika paljon käyttötapauksia tälle paketille. Valitettavasti meidän ei ollut yksi, koska meidän piti käsitellä PDF luominen backend puolella.

Vaihtoehto 2: Käytä vain PDF-kirjastoa

npm: ssä on useita kirjastoja tätä tarkoitusta varten, kuten jsPDF (edellä mainittu) tai PDFKit. Ongelmana niissä, että minun pitäisi luoda sivun rakenne uudelleen, jos haluan käyttää näitä kirjastoja. Tämä varmasti sattuu ylläpidettävyys, koska olisin tarvinnut soveltaa kaikkia myöhempiä muutoksia sekä PDF malli ja React sivu.

katso alla oleva koodi. Sinun täytyy luoda PDF-dokumentti itse käsin. Nyt voit kulkea DOM ja selvittää, miten kääntää kunkin elementin PDF niistä, mutta se on työlästä työtä. Täytyy olla helpompikin keino.

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

tämä pätkä on PDFKit-dokumenteista. Kuitenkin, se voi olla hyödyllistä, jos kohde on PDF-tiedosto heti eikä muuntaminen jo olemassa (ja alati muuttuva) HTML-sivu.

lopullinen vaihtoehto 3: nukketeatteri, Päätön Kromi solmulla.js

mikä on nukketeatteri? Dokumentaatio sanoo:

nukketeatteri on Solmukirjasto, joka tarjoaa korkean tason API: n kromin tai kromin ohjaamiseen DevTools-protokollan yli. Nukketeatteri toimii päätön oletusarvoisesti, mutta voidaan konfiguroida toimimaan täysi (Ei-päätön) Kromi tai Kromi.

se on periaatteessa selain, jota voi ajaa solmusta.js. Jos luet dokumentteja, ensimmäinen asia, jossa sanotaan nukketeatterin on, että voit käyttää sitä luoda kuvakaappauksia ja PDF-sivujen”. Loistavaa! Sitä me etsimme.

asennetaan nukketeatteri npmi i puppeteer, ja toteutetaan käyttötapaus.

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

Tämä on yksinkertainen toiminto, joka navigoi URL-osoitteeseen ja luo PDF-tiedoston sivustosta.

ensin käynnistämme selaimen (PDF-generointia tuetaan vain päättömässä tilassa), sitten avaamme uuden sivun, asetamme kuvaikkunan ja siirrymme annettuun URL-osoitteeseen.

asetettaessa waitUntil: ‘networkidle0’ valinta tarkoittaa, että nukketeatteri katsoo navigoinnin päättyneeksi, kun verkkoyhteyksiä ei ole vähintään 500 ms. (Tarkista API docs lisätietoja.)

sen jälkeen tallennamme PDF: n muuttujaksi, suljemme selaimen ja palautamme PDF: n.

Huomautus: page.pdfmenetelmä saa options objektin, jossa tiedoston voi tallentaa levylle myös ”polku” – valinnalla. Jos polkua ei ole, PDF-tiedostoa ei tallenneta levylle, saat sen sijaan puskurin. Myöhemmin pohdin, miten selviät siitä.)

Jos sinun on ensin kirjauduttava sisään luodaksesi PDF-tiedoston suojatulta sivulta, sinun on ensin navigoitava sisäänkirjautumissivulle, tarkastettava LOMAKEELEMENTIT tunnisteen tai nimen osalta, täytettävä ne ja lähetettävä sitten lomake:

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

säilytä kirjautumistiedot aina ympäristömuuttujissa, älä koodaa niitä!

Tyylimanipulaatio

nukketeatterilla on ratkaisu tähänkin tyylimanipulaatioon. Voit lisätä tyyli tunnisteet ennen tuottamista PDF, ja nukketeatterin luo tiedoston muutettu tyylejä.

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

Lähetä tiedosto asiakkaalle ja tallenna se

Okei, nyt olet luonut PDF-tiedoston taustajärjestelmään. Mitä tehdä nyt?

kuten edellä mainitsin, jos et tallenna tiedostoa levylle, saat puskurin. Sinun tarvitsee vain lähettää, että puskuri asianmukaisen sisällön tyyppi etupäähän.

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

nyt voit lähettää palvelimelle pyynnön, jolla saat luodun PDF-tiedoston.

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

kun olet lähettänyt pyynnön, puskurin pitäisi alkaa ladata. Nyt viimeinen vaihe on muuntaa puskuri PDF-tiedosto.

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>

That was it! Jos napsautat Tallenna-painiketta, selain tallentaa PDF-tiedoston.

nukketeatterin käyttäminen Dockerin kanssa

tämä taitaa olla toteutuksen kinkkisin osa – joten säästän sinut parin tunnin Googlailulta.

virallisissa asiakirjoissa todetaan, että ”päättömän Chromen saaminen käyntiin Dockerissa voi olla hankalaa”. Virallisissa dokumenteissa on Vianmääritysosa, josta kirjoitushetkellä löytyy kaikki tarvittavat tiedot nukketeatterin asentamisesta Dockerin kanssa.

Jos asennat nukketeatterin Alpine-kuvaan, varmista, että vierität hieman alas sivun tähän osaan. Muuten saatat kaunistella sitä, että et voi ajaa viimeisintä Nukketeatteriversiota ja sinun täytyy myös poistaa SHM-käyttö käytöstä lipulla:

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

muuten nukketeatterin alaprosessista saattaa loppua muisti ennen kuin se edes pääsee kunnolla käyntiin. Lisätietoja siitä vianmääritys linkki yllä.

vaihtoehto 3 + 1: CSS-Tulostussäännöt

voisi ajatella, että pelkkä CSS-tulostussääntöjen käyttäminen on kehittäjien näkökulmasta helppoa. Ei npm-moduuleja, vain puhdasta CSS: ää. Mutta miten he pärjäävät, kun se tulee cross-browser yhteensopivuus?

kun valitset CSS-tulostussääntöjä, sinun on testattava tulos jokaisessa selaimessa varmistaaksesi, että se tarjoaa saman asettelun, eikä se ole 100-prosenttinen.

esimerkiksi tauon lisäämistä tietyn elementin jälkeen ei voida pitää esoteerisena käyttötapauksena, mutta saatat yllättyä siitä, että sinun täytyy käyttää kiertoteitä saadaksesi tuon toimimaan Firefoxissa.

ellet ole taistelun kovettama CSS-taikuri, jolla on paljon kokemusta tulostettavien sivujen luomisesta, tämä voi olla aikaa vievää.

Tulostussäännöt ovat hyvät, jos voit pitää tulostustyylisivut yksinkertaisina.

Katsotaanpa esimerkkiä.

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

Tämä yllä oleva CSS piilottaa tulostuspainikkeen, ja lisää sivukatkon jokaisen div luokan content. kanssa on suuri artikkeli, joka tiivistää, mitä tulostussäännöillä voi tehdä, ja mitä vaikeuksia niillä on, mukaan lukien selaimen yhteensopivuus.

kaiken huomioiden CSS-tulostussäännöt ovat loistavia ja tehokkaita, jos haluat tehdä PDF: n ei niin monimutkaiselta sivulta.

Yhteenveto: PDF HTML: stä solmulla.js ja nukketeatteri

käydään siis nopeasti läpi vaihtoehdot, joita käsittelimme PDF-tiedostojen tuottamiseksi HTML-sivuilta:

  • kuvakaappaus DOM: stä: tästä voi olla hyötyä, kun haluat luoda tilannekuvia sivulta (esimerkiksi näytekuvan luomiseksi), mutta se jää lyhyeksi, kun sinulla on paljon dataa käsiteltävänä.
  • käytä vain PDF-kirjastoa: jos haluat luoda PDF-tiedostoja ohjelmallisesti tyhjästä, tämä on täydellinen ratkaisu. Muuten, sinun täytyy säilyttää HTML-ja PDF-malleja, joka on ehdottomasti no-go.
  • nukketeatteri: Vaikka se on suhteellisen vaikea saada toimimaan Docker, se antoi parhaan tuloksen meidän käyttötapaus, ja se oli myös helpoin kirjoittaa koodin.
  • CSS print rules: jos käyttäjät ovat tarpeeksi koulutettuja osaamaan tulostaa tiedoston ja sivut ovat suhteellisen yksinkertaisia, se voi olla kivuttomin ratkaisu. Kuten näit meidän jutussamme, se ei ollut.

hyvää painamista!

aiheeseen liittyviä aiheita

solmu.JS Tutorials for Beginners / @RisingStack