Articles

PDF létrehozása HTML-ből a Node segítségével.js pedig Bábjátékos

Boér Máté Kép's Picture

Boér Máté

a Teljes Stack Fejlesztő a RisingStack

ebben A cikkben megmutatom, hogyan lehet létrehozni egy PDF dokumentum egy erősen stílusú Reagálni oldal használata Csomópont.js, Puppeteer, headless Chrome & Docker.

háttér: Néhány hónappal ezelőtt a RisingStack egyik ügyfele arra kért minket, hogy fejlesszünk ki egy olyan funkciót, ahol a felhasználó PDF formátumban kérheti a React oldalt. Ez az oldal alapvetően egy jelentés / eredmény az adatvizualizációval rendelkező betegek számára, amely sok SVG-t tartalmaz. Továbbá, volt néhány speciális kérések, hogy manipulálják az elrendezés, valamint, hogy néhány átrendezése a HTML elemek. Tehát a PDF-nek az eredeti React oldalhoz képest eltérő stílusúnak és kiegészítésnek kell lennie.

mivel a hozzárendelés egy kicsit bonyolultabb volt, mint amit egyszerű CSS szabályokkal meg lehetett volna oldani, először megvizsgáltuk a lehetséges implementációkat. Lényegében 3 fő megoldást találtunk. Ez a blogpost végigvezeti Önt ezeken a lehetőségeken és a végső megvalósításokon.

egy személyes megjegyzés, mielőtt elkezdenénk: ez elég egy szóváltás, így csat fel!

Tartalomjegyzék:

  • Ügyféloldal vagy Háttéroldal?
  • 1. Lehetőség: Képernyőkép készítése a DOM-ból
  • 2. lehetőség: csak PDF könyvtárat használjon
  • végleges 3.lehetőség: bábos, fej nélküli Chrome csomóponttal.js
    • stílusmanipuláció
    • Fájl küldése az ügyfélnek, majd mentse el
  • a bábos segítségével Docker
  • 3 + 1 opció: CSS nyomtatási szabályok
  • összefoglaló

kliens oldal vagy szerver oldal?

lehetséges PDF fájl létrehozása mind az ügyféloldalon, mind a szerver oldalon. Azonban valószínűleg több értelme van, hogy hagyja, hogy a backend kezelje, mivel nem akarja használni az összes olyan erőforrást, amelyet a felhasználó böngészője kínálhat.

még így is, továbbra is megoldásokat mutatok mindkét módszerhez.

1. Lehetőség: Készítsen képernyőképet a DOM

első látásra, ez a megoldás a legegyszerűbbnek tűnt, igaznak bizonyult, de saját korlátai vannak. Ha nincsenek speciális igényei, mint például a választható vagy kereshető szöveg a PDF-ben, akkor ez egy jó és egyszerű módja annak, hogy létrehozzon egyet.

Ez a módszer egyszerű: hozzon létre egy képernyőképet az oldalról, majd tegye egy PDF fájlba. Elég egyszerű. Ehhez a megközelítéshez két csomagot használtunk:

Html2canvas, hogy képernyőképet készítsünk a DOM
jspdf-ből, egy könyvtárból, amely PDF-t generál

kezdjük a kódolást.

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

és ennyi!

győződjön meg róla, hogy vessen egy pillantást ahtml2canvasonclone módszer. Hasznos lehet, ha a kép elkészítése előtt gyorsan kell pillanatfelvételt készíteni és manipulálni a DOM-ot (pl. a Nyomtatás gomb elrejtése). Nagyon sok használati esetet látok ehhez a csomaghoz. Sajnos a miénk nem volt, mivel a PDF készítést a háttéroldalon kellett kezelnünk.

2. lehetőség: Csak egy PDF könyvtárat használjon

erre a célra több könyvtár van az NPM – en, mint például a jsPDF (fent említett) vagy a PDFKit. A probléma velük, hogy újra kell hoznom az oldal struktúráját, ha ezeket a könyvtárakat akarom használni. Ez határozottan fáj a karbantarthatóságnak, mivel minden későbbi változtatást alkalmaznom kellett volna mind a PDF sablonra, mind a React oldalra.

vessen egy pillantást az alábbi kódra. A PDF dokumentumot kézzel kell létrehoznia. Most már át lehet lépni a DOM-on, és kitalálni, hogyan lehet lefordítani az egyes elemeket PDF-re, de ez egy unalmas munka. Kell lennie egy könnyebb útnak.

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

Ez a töredék a PDFKit dokumentumokból származik. Hasznos lehet azonban, ha a cél egy PDF fájl, nem pedig egy már létező (és folyamatosan változó) HTML oldal konvertálása.

végleges opció 3: bábos, Fej Nélküli króm csomóponttal.js

mi a bábos? A dokumentáció szerint:

egy csomópont könyvtár, amely magas szintű API-t biztosít a Chrome vagy a Chromium vezérléséhez a DevTools protokoll felett. A Puppeteer alapértelmezés szerint fej nélkül fut, de konfigurálható teljes (nem Fej Nélküli) Chrome vagy Chromium futtatására.

Ez alapvetően egy böngésző, amely futtatható Node.js. Ha elolvassa a dokumentumokat, az első dolog, amit a Bábjátékosról mond, az az, hogy képernyőképeket és PDF-eket készíthet az oldalakról”. Kiváló! Ezt kerestük.

telepítsük anpmi i puppeteer – t, és hajtsuk végre a használati esetünket.

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

Ez egy egyszerű funkció, amely egy URL-re navigál, és létrehoz egy PDF fájlt a webhelyről.

először elindítjuk a böngészőt (a PDF generációt csak fej nélküli módban támogatjuk), majd megnyitunk egy új oldalt, beállítjuk a nézetablakot, majd navigálunk a megadott URL-re.

awaitUntil: ‘networkidle0’ beállítás azt jelenti, hogy a Puppeteer úgy véli, hogy a navigáció akkor fejeződik be, ha nincs legalább 500 ms hálózati kapcsolat. (ellenőrizze az API docs-kat további információkért.)

ezután a PDF fájlt egy változóba mentjük, bezárjuk a böngészőt, majd visszaadjuk a PDF fájlt.

Megjegyzés: Apage.pdfmetódusoptions objektumot kap, ahol a fájlt a “path” opcióval is lemezre mentheti. Ha az elérési út nincs megadva, a PDF nem kerül mentésre a lemezre, helyette puffert kap. Később, megbeszélem, hogyan tudja kezelni.)

abban az esetben, ha először be kell jelentkeznie egy PDF létrehozásához egy védett oldalról, először meg kell navigálnia a bejelentkezési oldalra, ellenőriznie kell az űrlap elemeit azonosító vagy név esetén, ki kell töltenie őket, majd be kell nyújtania az űrlapot:

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

mindig tárolja a bejelentkezési hitelesítő adatokat a környezeti változókban, ne kódolja őket!

Stílusmanipuláció

erre a stílusmanipulációra is van megoldás. A PDF létrehozása előtt stíluscímkéket helyezhet be, a Puppeteer pedig létrehoz egy fájlt a módosított stílusokkal.

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

küldje el a fájlt az ügyfélnek, majd mentse el

Oké, most létrehozott egy PDF fájlt a backend-en. Mi a teendő most?

ahogy fentebb említettem, ha nem menti a fájlt lemezre, puffert kap. Csak el kell küldenie azt a puffert a megfelelő tartalomtípussal az elejére.

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

most egyszerűen kérést küldhet a kiszolgálónak, hogy megkapja a generált PDF fájlt.

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

a kérés elküldése után a puffernek el kell kezdenie a letöltést. Most az utolsó lépés a puffer konvertálása PDF fájlba.

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>

ennyi volt! Ha rákattint a Mentés gombra, a PDF-fájlt a böngésző menti.

a Bábjátékos használata a Docker-rel

azt hiszem, ez a megvalósítás legnehezebb része-Tehát hadd mentsek meg néhány órányi Googlingot.

a hivatalos dokumentáció kimondja, hogy”a fej nélküli Chrome fel-és futtatása a Dockerben trükkös lehet”. A hivatalos dokumentumoknak van egy hibaelhárítási szakasza, ahol az írás idején megtalálhatja az összes szükséges információt a bábos telepítéséről a Docker segítségével.

Ha Puppeteer-t telepít az alpesi képre, győződjön meg róla, hogy kissé lefelé görgeti az oldal ezen részét. Ellenkező esetben előfordulhat, hogy nem tudja futtatni a legújabb bábos verziót, és az shm használatát is le kell tiltania egy zászló használatával:

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

ellenkező esetben a bábos alfolyamat elfogy a memóriából, mielőtt még megfelelően elindulna. További információ erről a fenti hibaelhárítási linken.

3 + 1. Lehetőség: CSS nyomtatási szabályok

azt gondolhatnánk, hogy a CSS nyomtatási szabályok egyszerű használata fejlesztői szempontból egyszerű. Nincs NPM modul, csak tiszta CSS. De hogyan boldogulnak, amikor a böngészők közötti kompatibilitásról van szó?

a CSS nyomtatási szabályok kiválasztásakor minden böngészőben tesztelnie kell az eredményt, hogy megbizonyosodjon arról, hogy ugyanazt az elrendezést biztosítja-e, és nem 100% – ban.

például egy szünet beillesztése egy adott elem után nem tekinthető ezoterikus Használati esetnek, mégis meglepődhet, hogy megoldásokat kell használnia a Firefox használatához.

hacsak nem csatakemény CSS bűvész, akinek sok tapasztalata van a nyomtatható oldalak létrehozásában, ez időigényes lehet.

nyomtatási szabályok nagy, ha meg tudja tartani a nyomtatási stíluslapok egyszerű.

nézzünk egy példát.

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

Ez a fenti CSS elrejti a nyomtatás gombot, és minden div után beszúr egy oldaltörést a content. van egy nagyszerű cikk, amely összefoglalja, hogy mit tehet a nyomtatási szabályokkal, és milyen nehézségek vannak velük, beleértve a böngésző kompatibilitását.

mindent figyelembe véve, a CSS nyomtatási szabályok nagyszerűek és hatékonyak, ha PDF-t szeretne készíteni egy nem olyan összetett oldalról.

összefoglaló: PDF HTML-ből csomóponttal.js pedig Bábjátékos

Szóval, nézzük gyorsan végig a lehetőségeket fedezett itt generáló PDF fájlokat HTML-oldalak:

  • Screenshot a DOM: Ez akkor lehet hasznos, amikor létre kell hozni pillanatképek egy oldalon (például, hogy hozzon létre egy miniatűr), de elmarad, ha sok adatot kell kezelni.
  • csak PDF könyvtárat használjon: ha PDF fájlokat programozottan kell létrehoznia a semmiből, ez tökéletes megoldás. Ellenkező esetben meg kell őriznie a HTML-és PDF-sablonokat, ami határozottan nem megy.
  • : Annak ellenére, hogy viszonylag nehéz volt a Docker-en dolgozni, ez adta a legjobb eredményt a használati esetünkhöz,valamint a legegyszerűbb kódot is írni.
  • CSS nyomtatási szabályok: ha a felhasználók elég képzettek ahhoz, hogy tudják, hogyan kell nyomtatni egy fájlt, és az oldalak viszonylag egyszerűek, ez lehet a leginkább fájdalommentes megoldás. Ahogy a mi esetünkben láttuk, nem volt.

Boldog nyomtatás!

kapcsolódó témák

csomópont.js oktatóanyagok kezdőknek / @RisingStack