Contact Info
Suit B3, House 5/9, Block B, Lalmatia
Dhaka, Bangladesh
info@xorgeek.com
Follow Us

PDF with jsPDF and AutoTable

generate pdf with jspdf

PDF with jsPDF and AutoTable

PDF generation is a common requirement in web applications. In our recent Angular-18 TypeScript project, We Xor Geek implemented it using jsPDF and AutoTable. While working on generate PDF with jsPDF, we faced several challenges, which we successfully resolved. Here’s a breakdown of the solutions for generate PDF with jsPDF.

1.Formatting Complex Table Header

Challenge:

  • The default AutoTable headers did not match the required design.

Solution:

  • Multi-Column Merging: The FFFF section spans 3 columns.
  • Multi-Row Headers: The 区分 (Category) and ラベル枚数 (Label Count) headers stretch across 2 rows for better clarity.
  • Line Breaks in Headers: The ラベル枚数 column includes \n for text wrapping.

Here is code:(Angular-18)


const headerRows = [[
   { content: '区    分', rowSpan: 2,colSpan: 3 },
   { content: '区画数', rowSpan: 2 },
   { content: 'FFFF', colSpan: 3 }, // Merges into 3 columns below
   { content: 'ラベル'+'\n'+'枚数',rowSpan: 2 },],
 [{ content: '基材(下地)材料名/品番' },{ content: '認定番号' },{ content: '使用量' }]];

2.Data Overflow in Columns

Challenge:

  • Some table content exceeded cell limits, causing text truncation.

Solution:

  • I enabled cellWidth and adjusted column width statically.

Here is code:(Angular-18)


columnStyles: {
0: { cellWidth: 47,fillColor: [255, 255, 255] },
1: { cellWidth: 87 },2: { cellWidth: 21 },3: { cellWidth: 43 },},

3.Ensuring a Minimum of 10 Rows

  • The API sometimes returned fewer than 10 rows, making the table inconsistent.
  • Blank rows were dynamically added to maintain a uniform table structure.
  • The last row contained totals for 区画 (sections), 使用量 (usage), and ラベル (labels).
  • These totals were calculated dynamically and inserted into the last row.

Here is code:(Angular-18)


   const rows = pdfdetails.map(item => [
// Dynamically setting the values for each row
   item.kubun_area === "1" || item.kubun_area === "3" ? "居 室" : "",
   item.kubun_kabe === "1" ? "天井" : item.kubun_kabe !== "1" ? "壁" : "",
   '  '+'  '+'  '+'    '+"階"+'\n'+item.kubun_kaisu,
   '  '+'   '+'   '+'    '+"区画"+'\n'+item.kukaku_num,
   item.sitaji_name + '\n' + item.merchandise_id,
   item.nintei_no,
   '   '+'   '+'   '+'    '+"㎡"+'\n'+item.shiyoryo,
   '   '+'   '+'   '+'   '+"枚"+'\n'+item.label_num
 ]);
const minRows = 11;
const rowsToAdd = minRows - rows.length;
for (let i = 0; i < rowsToAdd; i++) {
 rows.push([
   "","","","","","","","","","",]);}
 let kukaku_total=0
 let siyoryo_total=0
let label_num_total=0
pdfdetails.map(item => [
 kukaku_total += parseInt(String(item.kukaku_num), 10) || 0,
 siyoryo_total += parseInt(String(item.shiyoryo), 10) || 0,
 label_num_total += parseInt(String(item.label_num), 10) || 0
]);
rows[rows.length - 1] = [
"", "","",'   '+'   '+'   '+'  '+"区画"+'\n'+`${kukaku_total}`,
"","",'    '+'   '+'   '+'   '+"㎡"+'\n'+`${siyoryo_total}`,
'   '+'   '+'   '+'   '+"枚"+'\n'+`${label_num_total}`,];

4.Alignment and Styling Issues

Challenge:

  • The alignment of table content was inconsistent.

Solution:

  • Used halign and valign properties to ensure proper text positioning.

Here is code:(Angular-18)


bodyStyles: { fontSize: 11, textColor: 50, font: 'NotoSans',halign:'left',minCellHeight: rowHeight,valign: 'middle',cellPadding:.8},
headStyles: { fillColor: [255, 255, 255],valign: 'middle',halign:'center',font: 'NotoSans'}

5.Generate PDF:

Issue Faced:

  • Initially, the PDF was only displayed in an iframe and did not download automatically.
  • Users had to manually save the file, which was inconvenient.

Fix Implemented:

Here is Code:(Angular-18)


       const pdfBlob = doc.output("blob");
     	const pdfUrl = window.URL.createObjectURL(pdfBlob);
       const downloadLink = document.createElement("a");
       downloadLink.href = pdfUrl;
       downloadLink.download = `shinsei-${("00000000" +shinseiId).slice(-8
       )}-${this.formatDatePdf(new Date())}.pdf`; // Specify the file name
       downloadLink.style.display = "none"; // Hide the link
        document.body.appendChild(downloadLink);
        downloadLink.click();
        document.body.removeChild(downloadLink);
        setTimeout(() => {
          window.URL.revokeObjectURL(pdfUrl);
        }, 100);

Now, the PDF automatically downloads without requiring user interaction!

Final Thoughts

Using jsPDF and AutoTable in Angular-18 is a powerful solution for PDF generation. By addressing these challenges, I achieved a smooth, well-formatted PDF output. If you’re implementing a similar feature, consider optimizing styles, handling large tables efficiently, and leveraging customization options.