Reputation: 37
I am trying to generate a PDF that replicates the layout of my code using jsPDF in JavaScript. Previously, I attempted using html2canvas, but it converts the code layout into an image, resulting in larger file sizes and preventing text selection in the PDF.
Is there a way to directly make my code layout into a PDF using jsPDF while preserving text selectability and keeping file sizes smaller? I need the PDF to reflect the exact code structure, similar to how it appears in my application.
Here is my code and it's css
import React, { useRef, useState } from 'react'
import './PDFPreviewDialog.css'
import jsPDF from 'jspdf';
import IconButton from '../../../reusable/Buttons/IconButton/IconButton'
const PDFPreviewDialog = ({ onClose, docData }) => {
const contentRef = useRef(null);
const totalPages = Math.ceil(docData.length / 15);
const getCurrentMonthYear = () => {
const date = new Date();
return `${date.toLocaleString('default', { month: 'long' })} ${date.getFullYear()}`;
};
const [fileName, setFileName] = useState(`${getCurrentMonthYear()} Doctor list`);
const generatePDF = () => {
const input = contentRef.current;
const pdf = new jsPDF('p', 'mm', 'a4');
pdf.html(input, {
callback: function (doc) {
doc.autoPrint({variant: 'non-conform'});
doc.save(`${fileName}.pdf`);
},
x: 10,
y: 10,
windowWidth: input.offsetWidth,
windowHeight: input.offsetHeight
});
};
const handlePrint = () => {
const input = contentRef.current;
const pdf = new jsPDF('p', 'mm', 'a4');
pdf.html(input, {
callback: function (doc) {
doc.autoPrint();
window.open(doc.output('bloburl'), '_blank');
},
x: 10,
y: 10,
windowWidth: input.offsetWidth,
windowHeight: input.offsetHeight
});
};
return (
<div className="pdf-preview-dialog">
{/* Header */}
<div className="pdf-preview-header">
<IconButton
image={"/src/assets/x-mark.svg"}
bg={'#edeff2'}
onClick={onClose}
/>
<p className='pdf-preview-header-title'>Print by Doctor</p>
</div>
{/* Body */}
<div className="pdf-preview-body">
{/* No of Page and File Title */}
<div className="pdf-preview-info">
<div className="page-info">
<span className='page-info-no-of-page'>No. of Pages</span>
<span className='page-info-no-of-page-num'>{totalPages}</span>
</div>
<div className="file-name">
<input
type="text"
value={fileName}
onChange={(e) => setFileName(e.target.value)}
/>
</div>
<button className="edit-button">
<img src="/src/assets/pencil.svg" alt="Edit" />
</button>
</div>
{/* PDF Content */}
<div className="pdf-preview-content" ref={contentRef}>
{Array.from({ length: totalPages }, (_, i) => (
<PDFContent
key={i}
docData={docData.slice(i * 15, (i + 1) * 15)}
pageNumber={i + 1}
totalPages={totalPages}
/>
))}
</div>
{/* Footer DOWNLOAD - PRINT Buttons */}
<div className="pdf-preview-actions">
<button className='pdf-preview-actions-btns' onClick={handlePrint}>
<p>Print</p>
<img src="/src/assets/printer_white.svg" alt="printer" />
</button>
<button className="pdf-preview-actions-btns" onClick={generatePDF}>
<p>Download</p>
<img src="/src/assets/arrow-down-tray.svg" alt="download" />
</button>
</div>
</div>
</div>
);
};
const PDFContent = ({ docData, pageNumber, totalPages }) => {
return (
<div className="pdf-content">
{/* Header */}
<div className="pdf-header">
<h1>Kadodra Imaging Center</h1>
<p>Kadodra Chowkadi, Amreli, Surat, 395004</p>
<p>DR. Anushka Biswa</p>
</div>
{/* Doctor List */}
<table className="doctor-list">
<thead>
<tr>
<th>Name</th>
<th>Hospital</th>
<th>Patients</th>
<th>Revenue</th>
<th>Referral</th>
<th>Discount</th>
</tr>
</thead>
<tbody>
{docData.map((doc, index) => (
<tr key={index}>
<td>{doc.docName}</td>
<td>{doc.hospital}</td>
<td>{doc.patients}</td>
<td>{doc.revenue}</td>
<td>{doc.referral}</td>
<td>{doc.discount}</td>
</tr>
))}
</tbody>
</table>
<div className="page-number">
Page {pageNumber} of {totalPages}
</div>
</div>
);
};
export default PDFPreviewDialog;
CSS
.pdf-preview-dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
padding: 1.42rem 3.42rem;
border-radius: 1.42rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1.7rem;
}
/* Header */
.pdf-preview-header {
display: flex;
justify-content: space-between;
align-items: center;
/* margin-bottom: 20px; */
}
.pdf-preview-header-title{
color: #3C4453;
font-size: 1.14rem;
line-height: 1.7rem;
letter-spacing: 0.045rem;
}
/* Body */
.pdf-preview-body{
display: flex;
flex-direction: column;
gap: 1.42rem;
/* align-items: center; */
}
/* No of Page and File Title */
.pdf-preview-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.57rem 0.85rem;
border-radius: 1.7rem;
border: 1px solid #CDD1DB;
background-color: #fff;
/* margin-bottom: 20px; */
}
.page-info{
display: flex;
gap: 0.07rem;
border-radius: 0.57rem;
border: 1px solid #CDDDFE;
background-color: #F3F7FF;
font-size: 1.14rem;
line-height: 1.7rem;
letter-spacing: 0.045rem;
}
.page-info-no-of-page{
padding: 0.28rem 0.57rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.57rem;
background-color: #fff;
}
.page-info-no-of-page-num{
padding: 0.28rem 0.57rem;
font-weight: 300;
}
.file-name > input{
border: none;
outline: none;
color: #000;
font-size: 1.14rem;
line-height: 1.7rem;
letter-spacing: 0.045rem;
}
.edit-button{
padding: 0.57rem;
display: flex;
align-items: center;
justify-content: center;
border: none;
border-radius: 999px;
background-color: #DBDFE5;
}
.edit-button > img{
width: 1.42rem;
height: 1.42rem;
}
/* PDF Content */
.pdf-preview-content {
border: 1px solid #ccc;
padding: 20px;
margin-bottom: 20px;
max-height: 60vh;
overflow-y: auto;
}
.pdf-preview-pagination {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.pdf-preview-actions {
display: flex;
justify-content: center;
align-items: center;
gap: 1.7rem;
}
.pdf-preview-actions-btns{
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 0.57rem 1.14rem;
background-color: #0554F2;
border: 1px solid transparent;
gap: 0.28rem;
border-radius: 999px;
border-style: none;
cursor: pointer;
outline: none;
}
.pdf-preview-actions-btns p{
line-height: 1.39rem;
font-size: 0.95rem;
color: #F6F7F9;
}
.pdf-preview-actions-btns img{
width: 1.43rem;
height: 1.43rem;
}
/* Add more styles to match the design in the image */
Any guidance or examples demonstrating how to achieve this with jsPDF would be greatly appreciated. Thank you!
I want the PDF to have the same layout as my code renders and shows, maintaining the same layout in the PDF.
Upvotes: 0
Views: 263