Codder
Codder

Reputation: 37

How to maintain code layout in PDF using jsPDF in React.js?

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

Answers (0)

Related Questions