DEV Community

Cover image for PDF, Excel, Docx generate on React and Node js
Golam_Mostafa
Golam_Mostafa

Posted on • Updated on

PDF, Excel, Docx generate on React and Node js

Overview

  1. Create a React App
  2. Generate a single PDF document containing multiple tables on the front-end
  3. Generate a single Excel file containing multiple tables on the front-end
  4. Generate a PDF invoice on the back-end
  5. Generate a DOCX invoice on the back-end

Create React App

The first step is to create a new React app using the command npx create-react-app my-app and then navigate to the app directory using cd my-app and start the app using npm start.

Generate PDF tables

To generate PDF tables on the front-end, the following packages must be installed: jspdf and jspdf-autotable. These can be installed by running the command npm i jspdf jspdf-autotable.

Dependencies:

"dependencies": {
    "jspdf": "^2.5.1",
    "jspdf-autotable": "^3.5.28",
    "@material-ui/core": "^4.10.2",
    "@material-ui/icons": "^4.11.2",
    "material-table": "^1.69.2"
    ......
  }
Enter fullscreen mode Exit fullscreen mode

To copy sample data you can visit Link.

To create the data for the tables, you can use the sample data provided in the src/utils.js file. This file contains data for two tables, firstTableColumns and firstTableData, and secondTableColumns and secondTableData.

To trigger the download of the PDF, add a onClick event on a Download PDF button. In the function defined in the event, the following code will generate the PDF:

 const handleDownloadPDF = () => {
    const pdfDoc = new jsPDF();
    pdfDoc.text("Report", 15, 10);

    autoTable(pdfDoc, {
      theme: "grid",
      headStyles: { fontSize: 10 },
      bodyStyles: { fontSize: 8, fontStyle: "italic" },
      columns: firstTableColumns.map((col) => ({ ...col, dataKey: col.field })),
      body: firstTableData,
    });
    autoTable(pdfDoc, {
      theme: "grid",
      columns: secondTableColumns.map((col) => ({ ...col, dataKey: col.field })),
      body: secondTableData,
    });

    pdfDoc.save("ReportTable.pdf");
  };
Enter fullscreen mode Exit fullscreen mode

In the above function we have pdfDoc instance and we have pdfDoc.text(string, x, y). String to be added to the page. string on position x, y. x is Coordinate (in units declared at inception of PDF document) against left edge of the page. y Coordinate (in units declared at inception of PDF document) against upper edge of the page.

Then we have autotable which will import autoTable from "jspdf-autotable";

Styling Options
* theme: 'striped'|'grid'|'plain' = 'striped'
* styles: StyleDef
* headStyles: StyleDef
* bodyStyles: StyleDef
* footStyles: StyleDef
* alternateRowStyles: StyleDef
* columnStyles: {&columnDataKey: StyleDef}
Note that the columnDataKey is normally the index of the column, but could also be the dataKey of a column if content initialized with the columns property.

StyleDef:

*font: 'helvetica'|'times'|'courier' = 'helvetica'
*fontStyle: 'normal'|'bold'|'italic'|'bolditalic' = 'normal'
*overflow: 'linebreak'|'ellipsize'|'visible'|'hidden' = 'linebreak'
*fillColor: Color? = null
*textColor: Color? = 20
*cellWidth: 'auto'|'wrap'|number = 'auto'
*minCellWidth: number? = 10
*minCellHeight: number = 0
*halign: 'left'|'center'|'right' = 'left'
*valign: 'top'|'middle'|'bottom' = 'top'
*fontSize: number = 10
*cellPadding: Padding = 10
*lineColor: Color = 10
*lineWidth: border = 0 // If 0, no border is drawn
Color: Either false for transparent, hex string, gray level 0-255 or rbg array e.g. [255, 0, 0] false|string|number|[number, number, number]

Padding: Either a number or object {top: number, right: number, bottom: number, left: number}

border: Either a number or object {top: number, right: number, bottom: number, left: number}

We can add page number on pages. And here is full function body to generate pdf file with page number.

  const handleDownloadPDF = () => {
    const pdfDoc = new jsPDF();
    const totalPages = "{total_pages_count_string}";
    pdfDoc.page = 1;
    pdfDoc.text("Report", 15, 10);

    autoTable(pdfDoc, {
      theme: "grid",
      headStyles: { fontSize: 10 },
      bodyStyles: { fontSize: 8, fontStyle: "italic" },
      columns: firstTableColumns.map((col) => ({ ...col, dataKey: col.field })),
      body: firstTableData,
    });
    autoTable(pdfDoc, {
      theme: "grid",
      columns: secondTableColumns.map((col) => ({ ...col, dataKey: col.field })),
      body: secondTableData,

      addPageContent: (data) => {
        let footerStr = "Page " + pdfDoc.internal.getNumberOfPages();
        if (typeof pdfDoc.putTotalPages === "function") {
          footerStr = footerStr + " of " + totalPages;
        }
        pdfDoc.setFontSize(10);
        pdfDoc.text(
          footerStr,
          data.settings.margin.left,
          pdfDoc.internal.pageSize.height - 10
        );
      },
    });

    if (typeof pdfDoc.putTotalPages === "function") {
      pdfDoc.putTotalPages(totalPages);
    }

    pdfDoc.save("ReportTable.pdf");
  };
Enter fullscreen mode Exit fullscreen mode

For more options we have jspdf-autotable documentation here: Link

Generate Excel tables

To generate excel files on the front-end, you can use the npm packages xlsx and xlsx-populate. The packages can be installed by running the command npm i xlsx xlsx-populate.

Dependencies:

"dependencies": {
    "xlsx": "^0.17.0",
    "xlsx-populate": "^1.21.0",
    ......
}
Enter fullscreen mode Exit fullscreen mode

To create the data for the tables, you can use the sample data provided in the src/utils.js file. This file contains data for two tables, firstTableColumns and firstTableData, and secondTableColumns and secondTableData.

To trigger the download of the Excel file, you can create a button or a link that calls the downloadExcel() function when clicked.

const downloadExcel = () => {
    handleExport().then((url) => {
      const downloadAnchorNode = document.createElement("a");
      downloadAnchorNode.setAttribute("href", url);
      downloadAnchorNode.setAttribute("download", "report.xlsx");
      downloadAnchorNode.click();
      downloadAnchorNode.remove();
    });
  };
Enter fullscreen mode Exit fullscreen mode

Handle Export handleExport()

const handleExport = () => {
    let title = [{ A: "Excel Sheet 1" }].concat("");
    let table1 = [
      {
        A: "Name",
        B: "Email",
      },
    ];
    let table2 = [
      {
        A: "Name",
        B: "Email",
        C: "Year",
        D: "Fee",
      },
    ];
    firstTableData.forEach((row) => {
      table1.push({
        A: row.name,
        B: row.email,
      });
    });
    secondTableData.forEach((row) => {
      table2.push({
        A: row.name,
        B: row.email,
        C: row.year,
        D: row.fee,
      });
    });
    let table = [{ A: "Table Data" }]
      .concat(table1)
      .concat([""])
      .concat(table2);

    let finalData = [...title, ...table];

    const wb = XLSX.utils.book_new();

    // we will add multiple sheets

    // sheet 1
    const sheet1 = XLSX.utils.json_to_sheet(finalData, {
      skipHeader: true,
    });

    // sheet 2
    title = [{ A: "Excel Sheet 2" }].concat("");
    table = [{ A: "Table Data" }].concat(table2);
    finalData = [...title, ...table];

    const sheet2 = XLSX.utils.json_to_sheet(finalData, {
      skipHeader: true,
    });

    XLSX.utils.book_append_sheet(wb, sheet1, "First Sheet");
    XLSX.utils.book_append_sheet(wb, sheet2, "Second Sheet");

    const workbookBlob = workbook2blob(wb);

    let headerIndexes = [];
    finalData.forEach((data, index) =>
      data["A"] === "Name" ? headerIndexes.push(index) : null
    );
    const totalRecords = [...firstTableData, ...secondTableData].length;
    const dataInfo = {
      titleCell: "A2",
      titleRange: "A1:D2",
      tbodyRange: `A4:D${finalData.length}`,
      theadRange:
        headerIndexes?.length >= 1
          ? `A${headerIndexes[0]}:D${headerIndexes[0]}`
          : null,
      theadRange1:
        headerIndexes?.length >= 1
          ? `A${headerIndexes[0] + 1}:D${headerIndexes[0] + 1}`
          : null,
      tFirstColumnRange:
        headerIndexes?.length >= 1
          ? `A${headerIndexes[0] + 1}:A${totalRecords + headerIndexes[0] + 1}`
          : null,
    };

    return addStyle(workbookBlob, dataInfo);
  };
Enter fullscreen mode Exit fullscreen mode

Work Book to Blob workbook2blob(wb)

const workbook2blob = (workbook) => {
    const wopts = {
      bookType: "xlsx",
      bookSST: false,
      type: "binary",
    };

    const wbout = XLSX.write(workbook, wopts);
    const blob = new Blob([s2ab(wbout)], {
      type: "application/octet-stream",
    });

    return blob;
 };
Enter fullscreen mode Exit fullscreen mode

s2ab s2ab(wbout)

const s2ab = (s) => {
    const buf = new ArrayBuffer(s.length);
    const view = new Uint8Array(buf);

    for (let i = 0; i < s.length; ++i) {
      view[i] = s.charCodeAt(i);
    }

    return buf;
};
Enter fullscreen mode Exit fullscreen mode

Add Style addStyle(workbookBlob, dataInfo)

const addStyle = async (workbookBlob, dataInfo) => {
    const workbook = await XlsxPopulate.fromDataAsync(workbookBlob);
    workbook.sheets().forEach((sheet) => {
      sheet.usedRange().style({
        fontFamily: "Arial",
        verticalAlignment: "center",
      });

      sheet.column("A").width(15);
      sheet.column("B").width(25);

      sheet.range(dataInfo.titleRange).merged(true).style({
        bold: true,
        horizontalAlignment: "center",
        verticalAlignment: "center",
      });

      sheet.range(dataInfo.theadRange).merged(true).style({
        bold: true,
        horizontalAlignment: "center",
        verticalAlignment: "center",
      });

      sheet.range(dataInfo.theadRange1).style({
        bold: true,
        horizontalAlignment: "center",
        verticalAlignment: "center",
      });

      if (dataInfo.tbodyRange) {
        sheet.range(dataInfo.tbodyRange).style({
          horizontalAlignment: "center",
        });
      }
    });
    const workbookBlob_1 = await workbook.outputAsync();
    return URL.createObjectURL(workbookBlob_1);
  };
Enter fullscreen mode Exit fullscreen mode

For more, you can visit xlsx documentation xlsx. And explore this example Github line 60-219.

Generate PDF invoice (on back-end)

This code is for generating a PDF invoice on the back-end and downloading it on the front-end.

To trigger the download of the PDF file, you can create a button or a link that calls the downloadFromBackendPdf() function when clicked.

The downloadFromBackendPdf function is an async function that makes a GET request to a specified URL, in this case http://localhost:9000/pdf, to retrieve a PDF invoice.

Front-end:

Dependencies

"dependencies": {
     "axios": "^0.19.2",
     .....
    },
Enter fullscreen mode Exit fullscreen mode
const downloadFromBackendPdf = async () => {
    await axios
      .get("http://localhost:9000/pdf", { responseType: "blob" })
      .then((response) => {
        const href = URL.createObjectURL(
          new Blob([response.data], { type: "octet-stream" })
        );
        const a = Object.assign(document.createElement("a"), {
          href,
          style: "display: none",
          download: "Report.pdf",
        });
        document.body.appendChild(a);
        a.click();
        URL.revokeObjectURL(href);
        a.remove();
      })
      .catch((error) => {
        console.log(error);
      });
  };
Enter fullscreen mode Exit fullscreen mode

The function uses the axios library to make the GET request and specifies that the response should be of type blob. The function then creates a URL object using the URL.createObjectURL() method, passing in the blob data from the response.

A new <a> element is then created, and its href and download attributes are set to the URL object and the desired file name respectively. The element is then appended to the document's body, and the click() method is called on it, which triggers the download.

The URL object is then revoked using URL.revokeObjectURL(href) and the element is removed from the document's body.

It's important to note that the URL used in the GET request should be the correct URL of the back-end endpoint that serves the PDF invoice. Also, if the app is served in other than localhost, the domain should be changed accordingly.

Data for Invoice

const invoice = {
  invoice_no: "47-606-0116",
  created_date: "08/12/2022",
  due_date: "09/05/2022",
  company_name: "Oyoba",
  address: "9th Floor",
  payment_methods: [
    {
      type: "Check",
      value: 500,
    },
    {
      type: "Card",
      value: 400,
    },
  ],
  items_description: [
    {
      name: "Website Design",
      price: 400,
    },
    {
      name: "Hosting(3 months)",
      price: 100,
    },
    {
      name: "Domain Name",
      price: 70,
    },
  ],
};

module.exports = invoice;
Enter fullscreen mode Exit fullscreen mode

Invoice PDF content from

const invoice = require("./utils")

let total = 0;
let content = `
<div class="invoice-box" style="max-width: 800px;margin: auto;padding: 30px;border: 1px solid #eee;box-shadow: 0 0 10px rgba(0, 0, 0, .15);font-size: 16px;line-height: 24px;font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;color: #555;">
        <table cellpadding="0" cellspacing="0" style="width: 100%;line-height: inherit;text-align: left;">
            <tr class="top">
                <td colspan="2" style="padding: 5px;vertical-align: top;">
                    <table style="width: 100%;line-height: inherit;text-align: left;">
                        <tr>
                            <td class="title" style="padding: 5px;vertical-align: top;padding-bottom: 20px;font-size: 45px;line-height: 45px;color: #333;">
                                <img src="https://banner2.cleanpng.com/20190205/gqc/kisspng-computer-icons-invoice-clip-art-illustration-scala-close-the-books-faster-terminalmanager-emerson-5c595ed26cb4d9.7140607415493608504453.jpg" style="width:100%; max-width:300px;">
                            </td>

                            <td style="padding: 5px;vertical-align: top;text-align: right;padding-bottom: 20px;">
                                Invoice #: ${invoice.invoice_no}<br>
                                Created: ${invoice.created_date}<br>
                                Due: ${invoice.due_date}
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>

            <tr class="information">
                <td colspan="2" style="padding: 5px;vertical-align: top;">
                    <table style="width: 100%;line-height: inherit;text-align: left;">
                        <tr>
                            <td style="padding: 5px;vertical-align: top;padding-bottom: 40px;">
                                ${invoice.company_name}.<br>
                                ${invoice.address}
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>

            <tr class="heading">
                <td style="padding: 5px;vertical-align: top;background: #eee;border-bottom: 1px solid #ddd;font-weight: bold;">
                    Payment Method
                </td>

                <td style="padding: 5px;vertical-align: top;text-align: right;background: #eee;border-bottom: 1px solid #ddd;font-weight: bold;">
                    Amount
                </td>
            </tr>`
            for (const payment of invoice.payment_methods) {
                content += `<tr class="details">
                    <td style="padding: 5px;vertical-align: top;padding-bottom: 20px;">
                        ${payment.type}
                    </td>

                    <td style="padding: 5px;vertical-align: top;text-align: right;padding-bottom: 20px;">
                        $${payment.value}
                    </td>
                </tr>`
            }

            content += `<tr class="heading">
                <td style="padding: 5px;vertical-align: top;background: #eee;border-bottom: 1px solid #ddd;font-weight: bold;">
                    Item
                </td>

                <td style="padding: 5px;vertical-align: top;text-align: right;background: #eee;border-bottom: 1px solid #ddd;font-weight: bold;">
                    Price
                </td>
            </tr>`
            for (const item of invoice.items_description) {
                total += item.price;
                content += `
                <tr class="item">
                    <td style="padding: 5px;vertical-align: top;border-bottom: 1px solid #eee;">
                        ${item.name}
                    </td>

                    <td style="padding: 5px;vertical-align: top;text-align: right;border-bottom: 1px solid #eee;">
                        $${item.price}
                    </td>
                </tr>`
            }
            content += `
            <tr class="total">
                <td style="padding: 5px;vertical-align: top;"></td>

                <td style="padding: 5px;vertical-align: top;text-align: right;border-top: 2px solid #eee;font-weight: bold;">
                   Total: $${total}
                </td>
            </tr>
        </table>
    </div>
`
module.exports = content;
Enter fullscreen mode Exit fullscreen mode

Back-end:

First of all we need to install some packages by using npm i puppeteer cors express

Dependencies:

"dependencies": {
        "cors": "^2.8.5",
        "puppeteer": "^19.5.0",
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
    },

Enter fullscreen mode Exit fullscreen mode

app.js

const puppeteer = require("puppeteer");
const cors = require("cors");
const content = require('./pdfHtml');
const express = require("express");

const app = express();

app.use(
  cors({
    origin: "http://localhost:3000",// may vary depends on your app setup
  })
);

app.get("/pdf", async (req, res) => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setContent(content);

  const pdf = await page.pdf({ format: "A4" });
  res.send(Buffer.from(pdf));
  browser.close();
});

app.listen(9000, () => {
  console.log("Server is running");
});
Enter fullscreen mode Exit fullscreen mode

To start the server run node app.js.

The above code is for generating a PDF invoice on the back-end using the puppeteer library. The app.get("/pdf", async (req, res) => {} function is a GET endpoint that listens to requests at the route /pdf and uses the puppeteer library to generate a PDF invoice.

The puppeteer.launch() method is used to launch an instance of the browser. A new page is then created using browser.newPage().

The method await page.setContent(content) is used to set the content of the page to the content variable. The content variable should contain the HTML code that should be rendered in the PDF invoice.
You can visit Link to see the sample invoice html code.

const pdf = await page.pdf({ format: "A4" }); generates the PDF using the format specified in the options object (A4 in this case).

Finally, the generated PDF is sent as a response to the client using res.send(Buffer.from(pdf)), and the browser instance is closed using browser.close().

It's important to note that this code uses puppeteer library, which requires a running chrome browser in the machine where the back-end is running. Also, the Content variable should contain the correct HTML structure that needs to be rendered in the PDF.

Generate Docx CV (on back-end)

Docx template code is quite large for this documentation here is the Link to explore the code.

This code generates a docx file containing information about experiences, education, and skills.

Inputs

  • experiences: an array of objects, each containing information about a past or current job experience, including job title, company, start and end dates, and summary.
  • education: an array of objects, each containing information about a past education, including degree, field of study, school name, start and end dates, and notes.
  • skills: an array of objects, each containing information about a skill, including name.
  • PHONE_NUMBER, PROFILE_URL, EMAIL: constants containing personal contact information.

Outputs

  • A docx file containing the input information in a formatted manner.

Dependencies

  • The "docx" package, which can be installed via npm.

Now look at the DocumentCreator Class and it's properties

class DocumentCreator {
  create([experiences, educations, skills, achivements]) {
    const document = new Document({
      sections: [
        {
          children: [
            new Paragraph({
              text: "Full Name",
              heading: HeadingLevel.TITLE,
            }),
            this.createContactInfo(PHONE_NUMBER, PROFILE_URL, EMAIL),
            this.createHeading("Education"),
            ...educations
              .map((education) => {
                const arr = [];
                arr.push(
                  this.createInstitutionHeader(
                    education.schoolName,
                    `${education.startDate.year} - ${education.endDate.year}`
                  )
                );
                arr.push(
                  this.createRoleText(
                    `${education.fieldOfStudy} - ${education.degree}`
                  )
                );

                const bulletPoints = this.splitParagraphIntoBullets(
                  education.notes
                );
                bulletPoints.forEach((bulletPoint) => {
                  arr.push(this.createBullet(bulletPoint));
                });

                return arr;
              })
              .reduce((prev, curr) => prev.concat(curr), []),
            this.createHeading("Experience"),
            ...experiences
              .map((position) => {
                const arr = [];

                arr.push(
                  this.createInstitutionHeader(
                    position.company.name,
                    this.createPositionDateText(
                      position.startDate,
                      position.endDate,
                      position.isCurrent
                    )
                  )
                );
                arr.push(this.createRoleText(position.title));

                const bulletPoints = this.splitParagraphIntoBullets(
                  position.summary
                );

                bulletPoints.forEach((bulletPoint) => {
                  arr.push(this.createBullet(bulletPoint));
                });

                return arr;
              })
              .reduce((prev, curr) => prev.concat(curr), []),
            this.createHeading("Skills, Achievements and Interests"),
            this.createSubHeading("Skills"),
            this.createSkillList(skills),
            this.createSubHeading("Achievements"),
            ...this.createAchivementsList(achivements),
            this.createSubHeading("Interests"),
            this.createInterests(
              "Programming, Technology, Music Production, Web Design, 3D Modelling, Dancing."
            ),
            this.createHeading("References"),
            new Paragraph(
              "Dr. Dean Mohamedally Director of Postgraduate Studies Department of Computer Science, University College London Malet Place, Bloomsbury, London WC1E d.mohamedally@ucl.ac.uk"
            ),
            new Paragraph("More references upon request"),
            new Paragraph({
              text: "This CV was generated in real-time based on my Linked-In profile from my personal website www.dolan.bio.",
              alignment: AlignmentType.CENTER,
            }),
          ],
        },
      ],
    });

    return document;
  }

  createContactInfo(phoneNumber, profileUrl, email) {
    return new Paragraph({
      alignment: AlignmentType.CENTER,
      children: [
        new TextRun(
          `Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`
        ),
        new TextRun({
          text: "Address: 58 Elm Avenue, Kent ME4 6ER, UK",
          break: 1,
        }),
      ],
    });
  }

  createHeading(text) {
    return new Paragraph({
      text: text,
      heading: HeadingLevel.HEADING_1,
      thematicBreak: true,
    });
  }

  createSubHeading(text) {
    return new Paragraph({
      text: text,
      heading: HeadingLevel.HEADING_2,
    });
  }

  createInstitutionHeader(institutionName, dateText) {
    return new Paragraph({
      tabStops: [
        {
          type: TabStopType.RIGHT,
          position: TabStopPosition.MAX,
        },
      ],
      children: [
        new TextRun({
          text: institutionName,
          bold: true,
        }),
        new TextRun({
          text: `\t${dateText}`,
          bold: true,
        }),
      ],
    });
  }

  createRoleText(roleText) {
    return new Paragraph({
      children: [
        new TextRun({
          text: roleText,
          italics: true,
        }),
      ],
    });
  }

  createBullet(text) {
    return new Paragraph({
      text: text,
      bullet: {
        level: 0,
      },
    });
  }

  createSkillList(skills) {
    return new Paragraph({
      children: [
        new TextRun(skills.map((skill) => skill.name).join(", ") + "."),
      ],
    });
  }

  createAchivementsList(achivements) {
    return achivements.map(
      (achievement) =>
        new Paragraph({
          text: achievement.name,
          bullet: {
            level: 0,
          },
        })
    );
  }

  createInterests(interests) {
    return new Paragraph({
      children: [new TextRun(interests)],
    });
  }

  splitParagraphIntoBullets(text) {
    return text.split("\n\n");
  }

  createPositionDateText(startDate, endDate, isCurrent) {
    const startDateText =
      this.getMonthFromInt(startDate.month) + ". " + startDate.year;
    const endDateText = isCurrent
      ? "Present"
      : `${this.getMonthFromInt(endDate.month)}. ${endDate.year}`;

    return `${startDateText} - ${endDateText}`;
  }

  getMonthFromInt(value) {
    switch (value) {
      case 1:
        return "Jan";
      case 2:
        return "Feb";
      case 3:
        return "Mar";
      case 4:
        return "Apr";
      case 5:
        return "May";
      case 6:
        return "Jun";
      case 7:
        return "Jul";
      case 8:
        return "Aug";
      case 9:
        return "Sept";
      case 10:
        return "Oct";
      case 11:
        return "Nov";
      case 12:
        return "Dec";
      default:
        return "N/A";
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Properties

  • create([experiences, educations, skills, achivements]): a function that generates a document object containing the input information in a formatted manner.
  • createContactInfo(phoneNumber, profileUrl, email): a function that creates a paragraph containing contact information.
  • createHeading(text): a function that creates a heading with the specified text.
  • createSubHeading(text): a function that creates a subheading with the specified text.
  • createInstitutionHeader(institutionName, dateText): a function that creates a header for an institution with the specified name and date text.
  • createPositionDateText(startDate, endDate, isCurrent): a function that creates a text indicating the start and end date of a job position, and whether it's current or not.
  • createRoleText(role): a function that creates a text indicating a role.
  • splitParagraphIntoBullets(paragraph): a function that split a paragraph into bullet points.
  • createBullet(text): a function that creates a bullet with the specified text.
  • createSkillList(skills): a function that creates a list of skills.
  • createAchivementsList(achievements): a function that creates a list of achievements.
  • createInterests(interests): a function that creates a list of interests.

Now create an instance and generate a doc.

const documentCreator = new DocumentCreator();
const doc = documentCreator.create([
  experiences,
  education,
  skills,
  achievements,
]);

const b64string = Packer.toBase64String(doc);

module.exports = b64string;
Enter fullscreen mode Exit fullscreen mode

Purposes

  • The code creates a new instance of the DocumentCreator class and uses its create method to generate a document object.
  • The create method accepts an array of arrays containing the different sections of information, such as experiences, education, skills, and achievements.
  • The format and structure of the document object will depend on the implementation of the DocumentCreator class.
  • The code then uses the Packer.toBase64String function from the docx package to convert the document object to a base64 encoded string.
  • The base64 encoded string can be used to transmit the document to other systems or to be decoded and saved as a docx file.

Here is the back-end code

const cors = require("cors");
const express = require("express");
const app = express();
const b64string = require('./docxHtml');

app.use(
  cors({
    origin: "http://localhost:3000",
  })
);

app.get("/doc", async (req, res) => {
  res.setHeader("Content-Disposition", "attachment; filename=My Document.docx");
  res.send(Buffer.from(await b64string, "base64"));
});
Enter fullscreen mode Exit fullscreen mode

This code exports a base64 encoded string of a document object, and sets up a route on an express.js server to handle requests to download the document in the form of a docx file.

Purposes

  • The code exports the base64 encoded string representation of the document object.
  • When the route "/doc" is hit on the express.js server, the server sets the header of the response to indicate that the body is a downloadable attachment with the file name "My Document.docx".
  • The server then sends the body of the response as a buffer containing the decoded base64 encoded string of the document.
  • The route "/doc" can be accessed by making a GET request to the server at the specified endpoint.
  • The code uses the Buffer.from method to convert the base64 encoded string to a buffer so it can be sent as a response.

Here is the full project. You can clone the project

  • PDF generate
  • EXCEL generate
  • DOCX generate
  • git clone https://github.com/golammostafa13/generate-files.
  • cd generate-files
  • npm i
  • npm start

To start server:

  • cd server
  • npm i
  • npm start

Happy Coding :)

Top comments (0)