PDF, Excel, Docx generate on React and Node js
Tags: reactpdf, reactexcel, reactdocx, node
Published: 2023-01-19 07:29:33

# 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`:

```javascript
"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"
    ......
  }
```

To copy sample data you can visit [Link](https://github.com/golammostafa13/generate-files/blob/master/src/utils.js).

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:


```javascript
 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");
  };
```

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.

```javascript
  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");
  };
```
For more options we have jspdf-autotable documentation here: [Link](https://github.com/simonbengtsson/jsPDF-AutoTable)

# 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`:

```javascript
"dependencies": {
    "xlsx": "^0.17.0",
    "xlsx-populate": "^1.21.0",
    ......
}
```

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.

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

### Handle Export `handleExport()`

```javascript
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);
  };
```
### Work Book to Blob `workbook2blob(wb)`

```javascript
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;
 };
```

### s2ab `s2ab(wbout)`

```javascript
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;
};
```
### Add Style `addStyle(workbookBlob, dataInfo)`

```javascript
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);
  };
```
For more, you can visit `xlsx` documentation [xlsx](https://www.npmjs.com/package/xlsx). And explore this example [Github](https://github.com/golammostafa13/generate-files/blob/master/src/App.js) 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`
```javascript
"dependencies": {
     "axios": "^0.19.2",
     .....
    },
```

```javascript
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);
      });
  };
```
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
```javascript
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;
```
#### Invoice PDF content from 
```javascript
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;
```
#### Back-end:
First of all we need to install some packages by using `npm i puppeteer cors express`

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

```

`app.js`
```javascript
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");
});
```
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](https://github.com/golammostafa13/generate-files/blob/master/src/server/pdfHtml.js) 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](https://github.com/golammostafa13/generate-files/blob/master/src/server/docxHtml.js) 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

```javascript
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";
    }
  }
}
```
### 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. 
```javascript
const documentCreator = new DocumentCreator();
const doc = documentCreator.create([
  experiences,
  education,
  skills,
  achievements,
]);

const b64string = Packer.toBase64String(doc);

module.exports = b64string;
```
### 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 
```javascript
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"));
});
```

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 {% github golammostafa13/generate-files %}

- `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 :)