Aalam Info Solutions LLP · Follow
11 min read · Mar 5, 2024
In this tutorial, we’ll explore how to generate dynamic PDFs in a React application using jsPDF. We’ll focus on enhancing the PDF generation process by customizing AutoTable, implementing dynamic page numbering, and ensuring flexible page allocation.
STEPS:
1. Setup the React App
2. Install jsPDF
3. Install jspdf-autotable
4. Import Required Libraries and Components in App.js file
5. Define the generatePdf Function
6. Render the Main Content Using JSX
7. Create the pdfGenerator file component and Import Necessary Libraries
8. Sample Vendor and Items Data
9. Create a new jsPDF Instance and Set Properties
10. Add Images and Text to the PDF
11. Generate AutoTable for Item Details
12. Save (or) Open the PDF
13. Run the Application
Prerequisites
Before proceeding, check if Node.js and npm are already installed on your machine. Open your terminal and run the following commands.
Step 1: Setup the React App
Open your terminal and run the following commands to create a new React app.
npx create-react-app pdf-generator
cd pdf-generator
Step 2: Install jsPDF
Install the jsPDF library by running:
npm install jspdf
Step 3: Install jspdf-autotable
Install the jspdf-autotable library by running:
npm install jspdf-autotable
Step 4: Import Required Libraries and Components in App.js file
import React from 'react';
import { Button, DownloadIcon } from 'lumina-ui';
import PdfGenerator from './pdfGenerator';
Import the React library for building React components.
Import the Button and DownloadIcon components from the ‘lumina-ui’ library.
Import the PdfGenerator component from a local file named ‘pdfGenerator.js’.
Step 5: Define the generatePdf Function
const generatePdf = () => {
PdfGenerator();
}
Define a function named generatePdf responsible for triggering the PDF generation when the button is clicked.
Call the PdfGenerator function imported from ‘pdfGenerator.js’.
Step 6: Render the Main Content Using JSX
return (
<div style={{ justifyContent: "center", display: "flex", alignItems: "center", height: "100vh" }}>
<div>
<p>Click here to download the PDF file.</p>
<div style={{ display: "flex", justifyContent: "center" }}>
<Button
onClick={generatePdf}
icon={<DownloadIcon />}
type="button"
shape="rectangle"
size="small"
>
</Button>
</div>
</div>
</div>)
Use JSX to define the structure of the App component.
Create a div with inline styles that center its content both horizontally and vertically using flexbox.
Include a paragraph (<p>) with the text “Click here to download the PDF file.”
onClick: Specifies the generatePdf function to be executed when the button is clicked.
Step7: Create the pdfGenerator file component and Import Necessary Libraries
import jsPDF from 'jspdf';
import 'jspdf-autotable';
import { format } from 'date-fns';
Here, we import the required libraries: jsPDF for PDF generation, jspdf-autotable for creating tables in the PDF, and format from date-fns for formatting dates.
Step8: Sample Vendor and Items Data
const vendorData = {
// ... (vendor data, see previous code)
}
const itemsData = [
// ... (items data, see previous code)
];
Sample data for the vendor and items. You can replace this with dynamic data from your application.
Step9: Create a new jsPDF Instance and Set Properties
const pdf = new jsPDF();
pdf.setProperties({
title: "Request For Quotation"
});
Initialize a new jsPDF instance and set properties for the PDF, such as the title.
Step10: Add Images and Text to the PDF
pdf.addImage(imageUrl, 'JPEG', 10, 5, 40, 12);
This line of code adds an image to the RFQ PDF at a specific location with a defined size. The image is positioned 10 units from the left and 5 units from the top, and it has a width of 40 units and a height of 12 units.
pdf.setFontSize(10);
This line off code adds a font size.
pdf.setFont('custom', 'bold');
this line off code adds a font weight.
pdf.text('REQUEST FOR QUOTATION', 150, 12);
This line of code adds a title to the RFQ PDF at a specific location. The title is positioned 150 units from the left and 12 units from the top. Adjust these coordinates based on your layout preferences.
Step11: Generate AutoTable for Item Details
pdf.autoTable({
head: [itemDetailsHeaders],
body: itemDetailsRows,
startY: itemDetailsYStart, // Adjust the Y position as needed
headStyles: {
fillColor: headerStyles.fillColor,
textColor: headerStyles.textColor,
fontStyle: headerStyles.fontStyle,
fontSize: 10, // Adjust the font size as needed
font: 'Newsreader', // Set the font family
halign: 'left',
},
columnStyles: {
0: { cellWidth: columnWidths[0] }, // Adjust column widths as needed
1: { cellWidth: columnWidths[1] },
2: { cellWidth: columnWidths[2] },
3: { cellWidth: columnWidths[3] },
4: { cellWidth: columnWidths[4] },
},
alternateRowStyles: { fillColor: [255, 255, 255] },
bodyStyles: {
fontSize: 10, // Adjust the font size for the body
font: 'Newsreader', // Set the font family for the body
cellPadding: { top: 1, right: 5, bottom: 1, left: 2 }, // Adjust cell padding
textColor: [0, 0, 0], // Set text color for the body
rowPageBreak: 'avoid', // Avoid row page breaks
},
margin: { top: 10, left: 13 },
});
pdf.autoTable: This function is provided by the jsPDF-AutoTable library. It automatically generates a table based on the provided data and settings.
head: An array containing the headers of the table. In this case, it’s an array of strings representing the header names.
body: A 2D array containing the rows of the table. Each row is represented by an array of strings or numbers.
startY: The Y-axis position where the table should start. Adjust this based on the layout and positioning in your document.
headStyles: Styles for the table headers, including fill color, text color, font style, font size, font family, and horizontal alignment.
columnStyles: Styles for individual columns, allowing you to set specific widths for each column.
alternateRowStyles: Styles for alternating rows, providing a different fill color for better readability.
bodyStyles: Styles for the body of the table, including font size, font family, cell padding, text color, and avoiding row page breaks.
margin: Sets the top and left margin for the table.
Step12: Dynamic Page calculation and Page Numbers
const summaryYStart = pdf.internal.pageSize.getHeight() - 50;
pdf.internal.pageSize.getHeight(): This part retrieves the height of the current PDF page. The pdf.internal.pageSize object contains information about the dimensions of the page, and getHeight() specifically fetches the height of the page.
const totalPages = pdf.internal.getNumberOfPages();
for (let i = 1; i <= totalPages; i++) {
pdf.line(10, 283, 200, 283);
pdf.setPage(i);
pdf.setFont('Newsreader');
pdf.text(
`Page ${i} of ${totalPages}`,
185,
pdf.internal.pageSize.getHeight() - 5
);
}
const totalPages = pdf.internal.getNumberOfPages();: This line calculates the total number of pages in the generated PDF using the getNumberOfPages() method provided by jsPDF.
for (let i = 1; i <= totalPages; i++) This initiates a loop that iterates through each page of the PDF.
pdf.line(10, 283, 200, 283);: Draws a horizontal line at the bottom of each page. The line starts at coordinates (10, 283) and ends at (200, 283). This line serves as a separator between the content and the page number.
pdf.setPage(i);: Sets the current page to the ith page in the loop, allowing the following operations to be applied to the correct page.
pdf.setFont(‘Newsreader’);: Sets the font for the page number text. In this case, it uses the ‘Newsreader’ font.
pdf.text(Page ${i} of ${totalPages}, 185, pdf.internal.pageSize.getHeight() — 5);: Adds the page number text to the current page. It displays the current page number and the total number of pages at coordinates (185, pdf.internal.pageSize.getHeight() — 5). Adjust the coordinates and text formatting as needed for your layout.
This code ensures that each page of the PDF includes a horizontal line at the bottom and displays the page number and total number of pages in the specified font and position.
Step13: Save (or) Open the PDF
pdf.save(`RFQ.pdf`);const pdfDataUri = pdf.output('datauristring');
const newTab = window.open();
newTab?.document.write(`<iframe width='100%' height='100%' src='${pdfDataUri}'></iframe>`);
pdf.save(RFQ.pdf): This line of code saves the generated PDF with the filename “RFQ.pdf”. The save method is provided by the jsPDF library and triggers the browser’s download functionality to save the PDF file.
const pdfDataUri = pdf.output(‘datauristring’): The output method with the parameter ‘datauristring’ is used to obtain the data URI of the generated PDF. A data URI is a base64-encoded representation of the file, allowing it to be embedded directly into the HTML.
const newTab = window.open(): This code opens a new browser tab using the window.open() method. This new tab will be used to display the preview of the generated PDF.
newTab?.document.write(<iframe width=’100%’ height=’100%’ src=’${pdfDataUri}’></iframe>): The document.write method is used to write an HTML iframe element into the new tab’s document. The iframe is set to display the PDF using its data URI as the source (src).
Step 14: Run the Application
npm start
This command will start your application. Once the application is running, open your web browser and go to the specified localhost address (usually http://localhost:3000). You should be able to see your application running like this.
To download the PDF, click on the download button, and the browser will prompt you to save the file with the filename “RFQ.pdf”.
This step-by-step breakdown helps understand how the PdfGenerator component creates a detailed RFQ PDF document.
Below is the sample code for App.js:
import React from 'react';
import { Button, DownloadIcon } from 'lumina-ui';
import PdfGenerator from './pdfGenerator';const App = () => {
// Function to generate PDF when the button is clicked
const generatePdf = () => {
PdfGenerator();
}
// Render the main content
return (
<div style={{ justifyContent: "center", display: "flex", alignItems: "center", height: "100vh" }}>
<div>
<p>Click here to download the PDF file.</p>
<div style={{display:"flex",justifyContent:"center"}}>
<Button
onClick={generatePdf}
icon={<DownloadIcon />}
type="button"
shape="rectangle"
size="small"
>
</Button>
</div>
</div>
</div>
)
}
export default App
Below is the sample code for pdfGenerator.js:
import React from 'react';
import { Button, DownloadIcon } from 'lumina-ui';
import PdfGenerator from './pdfGenerator';const App = () => {
// Function to generate PDF when the button is clicked
const generatePdf = () => {
PdfGenerator();
}
// Render the main content
return (
<div style={{ justifyContent: "center", display: "flex", alignItems: "center", height: "100vh" }}>
<div>
<p>Click here to download the PDF file.</p>
<div style={{display:"flex",justifyContent:"center"}}>
<Button
onClick={generatePdf}
icon={<DownloadIcon />}
type="button"
shape="rectangle"
size="small"
>
</Button>
</div>
</div>
</div>
)
}
export default App
Below is the sample code for pdfGenerator.js:
import jsPDF from 'jspdf';
import 'jspdf-autotable';
import { format } from 'date-fns';
// Define the PdfGenerator component
const PdfGenerator = () => {
// Sample vendor data
const vendorData = {
vendorName: "Velavan B",
vendorAddress: "14/203, Kallakulam, Seenapuram",
vendorPinCode: "638057",
contactPerson: "Santhosh D",
contactPersonMobNo: "8993298712",
}
// Sample items data
const itemsData = [
{ itemName: 'Water Tanks', quantity: "15", uom: "Liters", unitPrice: "1200", total: (15 * 1200).toString() },
{ itemName: 'Laptops', quantity: "5", uom: "Pieces", unitPrice: "25000", total: (5 * 25000).toString() },
{ itemName: 'Coffee Mugs', quantity: "50", uom: "Pieces", unitPrice: "50", total: (50 * 50).toString() },
{ itemName: 'Desk Chairs', quantity: "8", uom: "Pieces", unitPrice: "8000", total: (8 * 8000).toString() },
{ itemName: 'LED TVs', quantity: "3", uom: "Units", unitPrice: "30000", total: (3 * 30000).toString() },
{ itemName: 'Bookshelves', quantity: "2", uom: "Units", unitPrice: "5000", total: (2 * 5000).toString() },
{ itemName: 'Smartphones', quantity: "10", uom: "Pieces", unitPrice: "15000", total: (10 * 15000).toString() },
{ itemName: 'Desk Lamps', quantity: "20", uom: "Pieces", unitPrice: "100", total: (20 * 100).toString() },
{ itemName: 'Headphones', quantity: "25", uom: "Pairs", unitPrice: "500", total: (25 * 500).toString() },
{ itemName: 'Backpacks', quantity: "12", uom: "Pieces", unitPrice: "800", total: (12 * 800).toString() },
{ itemName: 'Fitness Trackers', quantity: "7", uom: "Pieces", unitPrice: "1200", total: (7 * 1200).toString() },
{ itemName: 'Digital Cameras', quantity: "4", uom: "Units", unitPrice: "15000", total: (4 * 15000).toString() },
{ itemName: 'Portable Speakers', quantity: "18", uom: "Pieces", unitPrice: "800", total: (18 * 800).toString() },
{ itemName: 'Sunglasses', quantity: "30", uom: "Pairs", unitPrice: "200", total: (30 * 200).toString() },
{ itemName: 'Running Shoes', quantity: "15", uom: "Pairs", unitPrice: "1000", total: (15 * 1000).toString() },
{ itemName: 'Gaming Consoles', quantity: "6", uom: "Units", unitPrice: "25000", total: (6 * 25000).toString() },
{ itemName: 'Wristwatches', quantity: "9", uom: "Pieces", unitPrice: "3000", total: (9 * 3000).toString() },
{ itemName: 'Power Banks', quantity: "20", uom: "Pieces", unitPrice: "500", total: (20 * 500).toString() },
{ itemName: 'Bluetooth Earbuds', quantity: "22", uom: "Pairs", unitPrice: "1000", total: (22 * 1000).toString() },
{ itemName: 'Home Printers', quantity: "3", uom: "Units", unitPrice: "8000", total: (3 * 8000).toString() },
];
// Create a new jsPDF instance
const pdf = new jsPDF();
// Set document properties
pdf.setProperties({
title: "Request For Quotation"
})
// Add images and text to the PDF
const callImage = "/Calling.png";
const imageUrl = "/aalam.png";
pdf.addImage(imageUrl, 'JPEG', 10, 5, 40, 12);
pdf.setFontSize(10);
pdf.setFont('custom', 'bold');
pdf.text('REQUEST FOR QUOTATION', 150, 12);
// Line width in units (you can adjust this)
pdf.setLineWidth(0.1);
// Line color (RGB)
pdf.setDrawColor(200, 200, 200);
pdf.line(10, 18, 200, 18)
pdf.text('Contact Person', 13, 23)
pdf.setFont('custom', 'normal');
pdf.text("Nithish Kumar CP", 13, 28)
pdf.addImage(callImage, 'PNG', 13, 29, 3, 3);
pdf.text("9078382732", 16, 32)
pdf.setFont('Newsreader', 'bold')
pdf.text('RFQ No :', 130, 23)
pdf.text('RFQ Date :', 130, 27)
pdf.text('Due Date :', 130, 31)
pdf.setFont('Newsreader', 'normal')
pdf.text("RFQ20240092", 155, 23)
pdf.text(format(new Date(), 'MMM dd, yyyy'), 155, 27)
pdf.text(format(new Date("2024-02-08 00:00:00.000 +0530"), 'MMM dd, yyyy'), 155, 31)
pdf.line(10, 34, 200, 34)
pdf.setFont('Newsreader', 'bold')
pdf.text('To', 13, 39)
pdf.setFont('Newsreader', 'bold')
pdf.text('Purchase Centre Address :', 130, 39)
pdf.setFont('Newsreader', 'normal')
pdf.text('Head Office', 130, 44)
pdf.text('CHENNAI', 130, 48)
// Generate the vendor-specific content
pdf.setFont('Newsreader', 'bold');
pdf.text(`${vendorData?.vendorName}`, 13, 44);
pdf.text(`${vendorData?.vendorAddress}`, 13, 48)
pdf.setFont('Newsreader', 'normal');
pdf.text(`P.O BOX : ${vendorData?.vendorPinCode}`, 13, 52);
pdf.setFont('Newsreader', 'bold')
pdf.text('Contact Person', 13, 56)
pdf.setFont('Newsreader', 'normal')
pdf.text(`${vendorData?.contactPerson}`, 13, 60);
pdf.addImage(callImage, 'PNG', 13, 61, 3, 3);
pdf.text(` ${vendorData?.contactPersonMobNo || "N/A"}`, 16, 64);
pdf.setFont('Newsreader', 'bold')
pdf.text('Dear Sir,', 13, 72)
pdf.setFont('Newsreader', 'normal')
pdf.text('Please send your most competitive offer/mentioning your Terms & Conditions before the due date. You can send the same to \nthe above mentioned e-mail/fax', 13, 79)
pdf.setFont('Newsreader', 'normal')
pdf.setFontSize(10);
// Generate AutoTable for item details
const itemDetailsRows = itemsData?.map((item, index) => [
(index + 1).toString(),
item.itemName.toString(),
item.quantity?.toString(),
item.uom?.toString(),
item.total?.toLocaleString(),
]);
const itemDetailsHeaders = ['S.No', 'Item Name', 'Quantity', 'UOM', 'Total'];
const columnWidths = [15, 90, 30, 30, 23]; // Adjust column widths as needed
// Define table styles
const headerStyles = {
fillColor: [240, 240, 240],
textColor: [0],
fontFamily: 'Newsreader',
fontStyle: 'bold',
};
pdf.setFont('Newsreader');
const itemDetailsYStart = 88;
pdf.autoTable({
head: [itemDetailsHeaders],
body: itemDetailsRows,
startY: itemDetailsYStart, // Adjust the Y position as needed
headStyles: {
fillColor: headerStyles.fillColor,
textColor: headerStyles.textColor,
fontStyle: headerStyles.fontStyle,
fontSize: 10, // Adjust the font size as needed
font: 'Newsreader', // Set the font family
halign: 'left',
},
columnStyles: {
0: { cellWidth: columnWidths[0] }, // Adjust column widths as needed
1: { cellWidth: columnWidths[1] },
2: { cellWidth: columnWidths[2] },
3: { cellWidth: columnWidths[3] },
4: { cellWidth: columnWidths[4] },
},
alternateRowStyles: { fillColor: [255, 255, 255] },
bodyStyles: {
fontSize: 10, // Adjust the font size for the body
font: 'Newsreader', // Set the font family for the body
cellPadding: { top: 1, right: 5, bottom: 1, left: 2 }, // Adjust cell padding
textColor: [0, 0, 0], // Set text color for the body
rowPageBreak: 'avoid', // Avoid row page breaks
},
margin: { top: 10, left: 13 },
});
// Add summary and page numbers
const summaryYStart = pdf.internal.pageSize.getHeight() - 50;
pdf.setFont('Newsreader', 'noraml')
pdf.text('Thanking You,', 13, summaryYStart + 20)
pdf.text('Yours Faithfully,', 13, summaryYStart + 24)
pdf.text('For ', 13, summaryYStart + 28)
pdf.setFont('Newsreader', 'bold')
pdf.text('Aalam Info Solutions LLP', 19, summaryYStart + 28)
const totalPages = pdf.internal.getNumberOfPages();
for (let i = 1; i <= totalPages; i++) {
pdf.line(10, 283, 200, 283)
pdf.setPage(i);
pdf.setFont('Newsreader');
pdf.text(
`Page ${i} of ${totalPages}`,
185,
pdf.internal.pageSize.getHeight() - 5
);
}
// Save the PDF
pdf.save(`RFQ.pdf`);
// pdf open in a new tab
const pdfDataUri = pdf.output('datauristring');
const newTab = window.open();
newTab?.document.write(`<iframe width='100%' height='100%' src='${pdfDataUri}'></iframe>`);
}
export default PdfGenerator
— by Velavan Balaraman
About Us
Established in 2016, with the goal of being the company of right choice for clients, talents and solution providers. We design, develop and integrate software applications to meet the challenges of a dynamic business environment that suits our client’s needs. Today, we are one of the growing software service provider in the industry aimed at providing custom application development and suitable technical solution. Our team aspires to do quality work for our clients in providing end-to-end solutions and services at an affordable price .
Website : https://aalamsoft.com/
Follow us on,
LinkedIn : https://www.linkedin.com/company/aalam-info-solution-llp
Instagram : https://instagram.com/aalaminfo?igshid=YmMyMTA2M2Y=
Facebook : https://www.facebook.com/Aalam-Info-Solutions-LLP-775574966147738/