Articles

generování PDF z HTML s uzlem.js a Loutkář

Máté Boér Obrázek's Picture

Máté Boér

Full-Stack Developer na RisingStack

V tomto článku ukážu, jak si můžete vytvořit PDF dokument z těžce stylizovaný Reagovat stránku pomocí Uzlu.js, loutkář, bezhlavý Chrom & Docker.

pozadí: Před několika měsíci nás jeden z klientů RisingStack požádal o vývoj funkce, kde by uživatel mohl požádat o stránku React ve formátu PDF. Tato stránka je v podstatě zpráva / výsledek pro pacienty s vizualizací dat, obsahující mnoho svg. Kromě toho existovaly některé speciální požadavky na manipulaci s rozvržením a provedení některých přeskupení prvků HTML. PDF by tedy mělo mít odlišný styl a doplňky ve srovnání s původní stránkou React.

protože zadání bylo o něco složitější než to, co bylo možné vyřešit jednoduchými pravidly CSS, nejprve jsme prozkoumali možné implementace. V podstatě jsme našli 3 hlavní řešení. Tento blogpost vás provede těmito možnostmi a konečnými implementacemi.

osobní komentář, než začneme: je to docela hádka, tak se připoutejte!

obsah:

  • strana klienta nebo strana backendu?
  • možnost 1: vytvoření snímku obrazovky z DOM
  • možnost 2: Použijte pouze knihovnu PDF
  • poslední možnost 3: loutkář, Bezhlavý Chrome s uzlem.js
    • Styl manipulace
    • Odeslat soubor do klienta a uložit
  • Pomocí Loutkář s Docker
  • Možnost 3 +1: CSS print pravidel
  • Shrnutí

na straně Klienta nebo na straně Serveru?

je možné vygenerovat PDF soubor jak na straně klienta, tak na straně serveru. Pravděpodobně však dává větší smysl nechat to zpracovat backend, protože nechcete využívat všechny zdroje, které může prohlížeč uživatele nabídnout.

přesto budu stále zobrazovat řešení pro obě metody.

Možnost 1: Vytvořte snímek obrazovky z DOM

Na první pohled se toto řešení zdálo být nejjednodušší a ukázalo se, že je to pravda, ale má svá vlastní omezení. Pokud nemáte speciální potřeby, jako je volitelný nebo prohledávatelný text v PDF, je to dobrý a jednoduchý způsob, jak jej vygenerovat.

tato metoda je jednoduchá a jednoduchá: Vytvořte snímek obrazovky ze stránky a vložte jej do souboru PDF. Docela přímočaré. Použili jsme dva balíčky pro tento přístup:

Html2canvas, udělat screenshot z DOM
jsPdf, knihovna pro generování PDF

Pojďme začít kódování.

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

a je to!

ujistěte se, že se podíváte na metodu html2canvasonclone. Může se ukázat jako užitečné, když potřebujete rychle pořídit snímek a manipulovat s DOM (např. Vidím spoustu případů použití pro tento balíček. Bohužel náš nebyl jeden, protože jsme potřebovali zvládnout tvorbu PDF na straně backendu.

možnost 2: Používejte pouze knihovnu PDF

existuje několik knihoven tam na NPM pro tento účel, jako jsPDF (výše uvedené) nebo PDFKit. Problém s nimi, že bych musel znovu vytvořit strukturu stránky, kdybych chtěl tyto knihovny používat. To rozhodně bolí udržovatelnost, protože bych potřeboval aplikovat všechny následné změny jak na šablonu PDF, tak na stránku React.

podívejte se na níže uvedený kód. Dokument PDF musíte vytvořit sami ručně. Nyní můžete procházet DOM a zjistit, jak přeložit každý prvek do PDF, ale to je únavná práce. Musí existovat jednodušší způsob.

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

tento úryvek je z PDFKit dokumentů. Může však být užitečné, pokud je vaším cílem soubor PDF ihned a ne konverze již existující (a neustále se měnící) stránky HTML.

poslední možnost 3: loutkář, Bezhlavý chrom s uzlem.js

Co je loutkář? Dokumentace říká:

Puppeteer je knihovna uzlů, která poskytuje API vysoké úrovně pro ovládání Chrome nebo Chromium přes protokol DevTools. Puppeteer běží bez hlavy ve výchozím nastavení, ale může být nakonfigurován tak, aby spustit plný (non-headless) Chrome nebo Chromium.

je to v podstatě prohlížeč, který můžete spustit z uzlu.js. Pokud čtete dokumenty, první věc, kterou říká o Puppeteer je, že jej můžete použít ke generování screenshotů a PDF stránek‘. Výborně! To jsme hledali.

pojďme nainstalovat Puppeteer s npmi i puppeteer a implementovat náš případ použití.

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

Jedná se o jednoduchou funkci, která přejde na adresu URL a vygeneruje soubor PDF webu.

za Prvé, jsme se spustit prohlížeč (PDF generace podporovány pouze v headless režimu), pak jsme se otevřít novou stránku, nastavit výřez, a přejděte na uvedenou adresu URL.

Nastavení waitUntil: ‘networkidle0’ možnost znamená, že Loutkář se domnívá, navigace, aby být dokončena, když nejsou k dispozici žádné síťové připojení po dobu alespoň 500 ms. (Zkontrolujte, zda API docs pro další informace.)

poté uložíme PDF do proměnné, zavřeme prohlížeč a vrátíme PDF.

Poznámka: Metodapage.pdfpřijímá objekt options, kde můžete soubor uložit na disk s možností „cesta“. Pokud cesta není poskytnuta, PDF nebude uložen na disk, místo toho získáte vyrovnávací paměť. Později, diskutuji o tom, jak to zvládnete.)

V případě, že budete muset nejprve přihlásit ke generování PDF z chráněné stránky, budete muset nejprve přejděte na přihlašovací stránku, zkontrolujte prvky formuláře pro ID, nebo jméno, vyplňte je v, pak odeslání formuláře:

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

přihlašovací údaje vždy ukládejte do proměnných prostředí,nekódujte je!

stylová manipulace

loutkář má řešení i pro tuto stylovou manipulaci. Před vygenerováním PDF můžete vložit značky stylů a Puppeteer vygeneruje soubor s upravenými styly.

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

Odeslat soubor klientovi a uložit jej

dobře, nyní jste vygenerovali soubor PDF na backendu. Co teď dělat?

jak jsem zmínil výše, pokud soubor neuložíte na disk, získáte vyrovnávací paměť. Stačí poslat vyrovnávací paměť se správným typem obsahu na front-end.

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

nyní můžete jednoduše odeslat požadavek na server, abyste získali vygenerovaný PDF.

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

jakmile odešlete požadavek, buffer by se měl začít stahovat. Nyní je posledním krokem převést vyrovnávací paměť do souboru 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>

to bylo ono! Pokud kliknete na tlačítko Uložit, prohlížeč uloží PDF.

pomocí loutkář s Docker

myslím, že to je nejsložitější část implementace – tak dovolte mi, abych vám ušetřit pár hodin Googling.

oficiální dokumentace uvádí, že „spuštění bezhlavého Chromu v Dockeru může být obtížné“. Oficiální dokumenty mají sekci Řešení problémů, kde v době psaní najdete všechny potřebné informace o instalaci puppeteer s Docker.

Pokud nainstalujete Puppeteer na obrázek Alpine, ujistěte se, že jste trochu posunuli dolů na tuto část stránky. Jinak, možná budete zakrývat skutečnost, že nelze spustit nejnovější Loutkář verzi a budete také muset vypnout shm použití, pomocí vlajky:

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

Jinak, Loutkář sub proces může spustit z paměti dříve, než se dokonce dostane začalo správně. Více informací o tom na výše uvedeném odkazu na řešení problémů.

možnost 3 + 1: pravidla tisku CSS

jeden by si mohl myslet, že jednoduché použití pravidel tisku CSS je snadné z hlediska vývojářů. Žádné moduly NPM, jen čistý CSS. Ale jak se jim daří, pokud jde o kompatibilitu mezi prohlížeči?

při výběru pravidel tisku CSS musíte výsledek otestovat v každém prohlížeči, abyste se ujistili, že poskytuje stejné rozvržení, a není to 100%.

například, vložení přestávky po daný prvek nelze považovat za esoterické případ použití, ale můžete být překvapeni, že budete muset použít zástupná řešení, aby si to pracovat ve Firefoxu.

Pokud nejste kouzelník CSS s mnoha zkušenostmi s vytvářením stránek, které lze tisknout, může to být časově náročné.

pravidla tisku jsou skvělé, pokud můžete udržet tiskové styly jednoduché.

podívejme se na příklad.

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

CSS výše skryje tlačítko tisk, a vloží zalomení stránky po každý div s třídou content. Zde je skvělý článek, který shrnuje, co můžete dělat s potiskem pravidla, a jaké jsou problémy s nimi, včetně kompatibility prohlížečů.

Vezmeme-li v úvahu vše, pravidla tisku CSS jsou skvělá a efektivní, pokud chcete vytvořit PDF z ne tak složité stránky.

shrnutí: PDF z HTML s uzlem.js a Loutkář

Tak pojďme rychle projít možnosti jsme probrali tady pro generování PDF souborů z HTML stránky:

  • Screenshot z DOM: To může být užitečné, když potřebujete vytvořit snímky ze stránky (například vytvořit náhledy), ale zaostává, když máte hodně dat zvládnout.
  • používejte pouze knihovnu PDF: Pokud potřebujete vytvářet soubory PDF programově od nuly, je to perfektní řešení. V opačném případě musíte udržovat šablony HTML a PDF, což je rozhodně ne-go.
  • loutkář: Přesto, že je relativně obtížné, aby to fungovalo na Dockeru, poskytlo to nejlepší výsledek pro náš případ použití,a bylo také nejjednodušší napsat kód.
  • CSS print pravidla: Pokud uživatelé jsou vzdělaní dost vědět, jak tisknout do souboru a vaše stránky jsou poměrně jednoduché, to může být nejvíce bezbolestné řešení. Jak jste viděli v našem případě, nebylo.

Šťastný tisk!

Související témata

uzel.JS návody pro začátečníky / @ RisingStack