How to Generate PDF Certificates from HTML in Node.js
In this post, you’ll learn how to generate PDF certificates from HTML using our Node.js PDF Generator API. With our API, you receive 100 credits with the free plan. Different operations on a document consume different amounts of credits, so the number of PDF documents you can generate may vary. To access your API key, sign up for a free account.
Certificates are most commonly generated by educational institutions, online course platforms, and corporate training programs. PDF certificates can automatically be generated when a student or employee successfully completes their program. To further streamline the process, a student’s name, program, and completion date can be pulled from a database and dynamically inserted into the certificate.
To help you get started, we’ve provided a free certificate template in HTML and CSS that can be customized to meet your specific requirements. You can easily style your certificate by updating the CSS file with your own custom images and fonts.
Requirements
To get started, you’ll need:
To access your PSPDFKit API key, sign up for a free account. Your account includes 100 credits, which can be used for various document operations. Once you’ve signed up, you can find your API key in the Dashboard > API Keys section.
Setup
Download the certificate template and extract the contents of the ZIP file into a folder. You’ll get an HTML file, Inter fonts, a Space Mono font, SVG logos, and a README file.
Open the folder in your preferred code editor. Then, open the terminal.
-
Create a new Node.js project by running:
npm init --yes
This will create a package.json
file in the root of your project.
-
Now, to interact with the PSPDFKit PDF Generator API, install Axios, which is an HTTP client for Node.js, and Form-Data, which is a library for converting file data to
multipart/form-data
streams.
Run these commands to install Axios and Form-Data:
npm install axios npm install form-data
Creating a CSS File
If you open the index.html
file, you’ll see the styles between the opening and closing <style>
tag. Copy the styles to a new file called style.css
, and save it in the same folder:
@font-face { font-family: 'Inter'; src: url('Inter-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Inter'; src: url('Inter-Medium.ttf') format('truetype'); font-weight: 500; font-style: normal; } @font-face { font-family: 'Inter'; src: url('Inter-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } @font-face { font-family: 'Space Mono'; src: url('SpaceMono-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } body { font-size: 0.875rem; font-family: 'Inter', sans-serif; font-weight: 400; color: #000000; text-align: center; margin: 0 auto; position: relative; } #pspdfkit-header { font-size: 0.625rem; text-transform: uppercase; letter-spacing: 2px; font-weight: 700; color: #717885; margin-top: 2.5rem; margin-bottom: 2.5rem; } .logo { margin-top: 2.5rem; height: 1.5rem; width: auto; } h1 { font-family: 'Space Mono', monospace; font-size: 2.25rem; font-weight: 400; margin-top: 0; } h4 { font-family: 'Space Mono', monospace; font-size: 1rem; font-weight: 400; } .page { margin-left: 5rem; margin-right: 5rem; } .form { display: flex; flex-direction: column; margin-top: 6rem; } .border-bottom { border: 1px solid #000000; border-top: none; border-left: none; border-right: none; } .signer { display: flex; justify-content: space-between; align-items: center; margin: 2rem 0 2rem 0; } .signer-item { flex-grow: 1; } input { color: #4537de; font-family: 'Space Mono', monospace; font-size: 1rem; text-align: center; margin-top: 4rem; height: 4rem; width: 100%; box-sizing: border-box; } input#name { font-size: 1.5rem; } #pspdfkit-footer { font-size: 0.5rem; text-transform: uppercase; letter-spacing: 1px; font-weight: 500; color: #717885; margin-top: 2.5rem; bottom: 2.5rem; position: absolute; width: 100%; } .footer-columns { display: flex; justify-content: space-between; padding-left: 2.5rem; padding-right: 2.5rem; }
To access the styles from index.html
, use the <link>
tag. While referring to the stylesheet file, just use the name of the file and don’t create nested paths:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Certificate</title> <link rel="stylesheet" href="style.css" /> </head> </html>
Interacting with the API
Now, import the axios
, form-data
, and fs
modules to the index.js
file using the require()
function. Place the index.js
file in the root directory of your project:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs');
Preparing the Payload
Create the FormData
object with the constructor. This constructor returns an empty FormData
object:
const formData = new FormData();
FormData
has a method called append
, and it takes key-value pairs. The value
can be a string, a binary large object (blob), or a file stream.
Since you’re sending files and images that are binary data, use the Node.js fs
module to read the files. Then, append it to the FormData
object.
You’re passing the instructions
key with the value of a JSON stringified object that contains your html
file and assets
like styles, fonts, and SVG files. The instructions
JSON is referencing files by the name of their parts that are appended in the form data as files:
const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { html: 'index.html', assets: [ 'style.css', 'Inter-Regular.ttf', 'Inter-Medium.ttf', 'Inter-Bold.ttf', 'SpaceMono-Regular.ttf', 'logo.svg', 'ribbon.svg', ], }, ], }), ); formData.append('index.html', fs.createReadStream('index.html')); formData.append('style.css', fs.createReadStream('style.css')); formData.append( 'Inter-Regular.ttf', fs.createReadStream('Inter-Regular.ttf'), ); formData.append( 'Inter-Medium.ttf', fs.createReadStream('Inter-Medium.ttf'), ); formData.append( 'Inter-Bold.ttf', fs.createReadStream('Inter-Bold.ttf'), ); formData.append( 'SpaceMono-Regular.ttf', fs.createReadStream('SpaceMono-Regular.ttf'), ); formData.append('logo.svg', fs.createReadStream('logo.svg')); formData.append('ribbon.svg', fs.createReadStream('ribbon.svg'));
Making the Request with Axios
Now, make a POST
request to the https://api.pspdfkit.com/build
endpoint using the axios
library. Also, don’t forget to replace YOUR_API_KEY
with your API key:
(async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { if (e.response) { const errorString = await streamToString(e.response.data); console.log(errorString); } else { console.log(e); } } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Here, you’re setting the responseType
to stream
to get the response as a readable stream; hence, you can pipe the response into a writable file stream named result.pdf
.
Generating the PDF
Now, run the Node script from the terminal. This’ll generate the result.pdf
file:
node index.js
You can see the full JavaScript code here:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { html: 'index.html', assets: [ 'style.css', 'Inter-Regular.ttf', 'Inter-Medium.ttf', 'Inter-Bold.ttf', 'SpaceMono-Regular.ttf', 'logo.svg', 'ribbon.svg', ], }, ], }), ); formData.append('index.html', fs.createReadStream('index.html')); formData.append('style.css', fs.createReadStream('style.css')); formData.append( 'Inter-Regular.ttf', fs.createReadStream('Inter-Regular.ttf'), ); formData.append( 'Inter-Medium.ttf', fs.createReadStream('Inter-Medium.ttf'), ); formData.append( 'Inter-Bold.ttf', fs.createReadStream('Inter-Bold.ttf'), ); formData.append( 'SpaceMono-Regular.ttf', fs.createReadStream('SpaceMono-Regular.ttf'), ); formData.append('logo.svg', fs.createReadStream('logo.svg')); formData.append('ribbon.svg', fs.createReadStream('ribbon.svg')); (async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { if (e.response) { const errorString = await streamToString(e.response.data); console.log(errorString); } else { console.log(e); } } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Conclusion
In this post, you generated a PDF certificate from an HTML template using our Node.js PDF generation API. We created similar PDF certificate generation blog posts using sample code from other programming languages:
In addition to templates for generating certificates, we created free templates for other commonly used documents, like receipts, invoices, and reports. If you’re interested in generating other types of documents in Node.js, check out the following posts:
- Generating PDF receipts using Node.js
- Generating PDF invoices using Node.js
- Generating PDF reports using Node.js
All our templates are available for you to download on our PDF Generator API page. Feel free to customize or add any CSS to the template to fit your use case or help reflect your company’s brand.