DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Essential Security Measures for PDF Documents
  • Building a PDF Viewer From Scratch
  • Google Cloud Document AI Basics
  • Thumbnail Generator Microservice for PDF in Spring Boot

Trending

  • How to Build Local LLM RAG Apps With Ollama, DeepSeek-R1, and SingleStore
  • A Guide to Container Runtimes
  • Segmentation Violation and How Rust Helps Overcome It
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  1. DZone
  2. Coding
  3. JavaScript
  4. Template-Based PDF Document Generation in JavaScript

Template-Based PDF Document Generation in JavaScript

A guide to creating PDF documents from JSON/XML/Database in your js application using eDocGen.

By 
Venkatesh Rajendran user avatar
Venkatesh Rajendran
DZone Core CORE ·
Aug. 24, 22 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
10.6K Views

Join the DZone community and get the full member experience.

Join For Free

Document generation is a very common requirement in the life of a developer. Whether it is an e-commerce site, Management app, or anything. It can be invoice generation, insurance document preparation, doctor's prescription, HR Offer generation, and Payslip generation and you could think of tons of use cases.  There always will be a need for document generation.

From a developer's perspective, there are a few common approaches to getting this job done. 

  1. Create HTML elements and print them to generate the documents
  2. Using some library to generate the documents
  3. Letting the server handle document generation based on a static template

These approaches didn't help me. The clients want to have their documents customized by themself. I have been searching for an approach and found eDocGen as a single-point solution. 

Unlike other services, eDocGen provides RestAPI that can be integrated into our application.

In this article, we will discuss how we can integrate eDocGen into our js application to generate documents from various data formats like JSON/XML/Database schema. Please get your free trial to get started with the coding.

Let's dive in and write code.

Project Setup

For the demo purpose, I have created a sample js application that runs on Node.js. 

Please follow the step below to set up a coding playground for us.

Step 1:

Use npm init to create package.json

Step 2:

Add axios, form-data, request, xhr2 dependencies needed for developing this application using npm install axios form-data request xhr2

Step 3:

We need an index file that will act as the starting point of our application. Create an index.js file in the root directory and modify package.json like below.

JSON
 
"scripts": {
    "start": "node index.js"
  }


Now we have a basic application to start with. End of these steps, the package.json should look like something below.

JSON
 
{
  "name": "nodejs-multiple-upload-files",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "axios": "^0.27.2",
    "form-data": "^4.0.0",
    "request": "^2.88.2",
    "xhr2": "^0.2.1"
  }
}


Login

Though the article is about document generation, we need to log in to get our access token. It's a typical JWT token that will be used for authorizing the document generation API.

JavaScript
 
var XMLHttpRequest = require("xhr2");
var xhr = new XMLHttpRequest();

module.exports.getToken = function (callback) {
  var data = JSON.stringify({
    username: "<your username>",
    password: "<password>",
  });

  xhr.addEventListener("readystatechange", function () {
    if (this.readyState === 4) {
      token = JSON.parse(this.responseText).token;

      console.log("User Token", token);
      callback(token);
    }
  });

  xhr.open("POST", "https://app.edocgen.com/login");
  xhr.setRequestHeader("content-type", "application/json");
  xhr.setRequestHeader("cache-control", "no-cache");
  xhr.send(data);
};


We can cache the token in the application for a time period less than the expiration time and use it to generate documents or upload templates. After the expiration time, we can refresh the token. The cache can be Redis or an in-memory cache. It's up to your application design.

Template Design

As explained above, eDocGen lets the users customize and upload the templates. But how will the data be mapped dynamically? There are certain rules that map the data to the document. We will see how to create a template with the rules.

Take a look at this document.

document

eDocGen uses tags that are enclosed by {} for the dynamic fields. We can dynamically add text, logos, tables, conditional statements, mathematical calculations, etc.

For example, in the image above, 

String fields: {Invoice_Number} and {Invoice_Date} are configured to be replaced as text in the template. Anything inside {} in the template will be matched with input data and replaced.

Dynamic Tables: Dynamic tables would be a go-on option when there is an array of data that needs to be looped and replaced in a table. The row in the table starts with {#tablename} and ends with {/tablename}. In the example above, a row in the invoice table is started with {#IT} in the first column and ended with {/IT} in the last column. Columns in the rows can have String fields. In our example, {Item_description} and {Amount}

Image: eDocGen offers to add images dynamically to the template. Follow the steps below. 

  • Upload the image to the eDogGen that should respond with an image_id.
  • {%image_id} is the tag for populating images. The image will be fetched by image_id from eDocGen storage and replaced in the place of {%image_id}. The image_id is expected to be present in the input data.

Conditional-based Dynamic fields (If-Else): Displaying content conditionally can be done with the Conditional tags. For example, When the language is English, {#language == "english"} English content will be displayed in the document. Likewise, multiple languages can be supported in a single document template.

Mathematical calculations: eDocGen supports mathematical calculations based on formulae defined in the template. The summation of the amount of the items in the invoice can be calculated using the below formulae.

JSON
 
{
    IT // array of items
    | summation:'Amount' // value that needs to be used for calculation 
    | format_number: ",” // format of the value
}


Please head over to JSON-to-pdf for more details.

Template Upload

Once the template is prepared, this can be uploaded for consumption. There are two ways. 

  1. eDocGen's interactive UI - which has integration with dropbox, drive, Evernote
  2. eDocGen's RestAPI - which can be integrated into the client code for uploading templates.

For the demo, purpose I have used the UI for uploading the template. On the successful upload, we get an ID as a response. This is the ID that will be used for generating the documents.

Upload a template

Leaving here the Upload API structure for your reference, in case you wish to use API.

JSON
 

"/api/v1/document": {
  "post": {
    "tags": [
      "Document"
    ],
    "description": "Upload template to eDocGen",
    "produces": [
      "application/json"
    ],
    "consumes": [
      "multipart/form-data"
    ],
    "parameters": [
      {
        "name": "documentFile",
        "description": "file to upload",
        "required": true,
        "type": "file",
        "in": "formData"
      },
      {
        "name": "x-access-token",
        "in": "header",
        "description": "JWT auth token from login",
        "required": true,
        "type": "string"
      }
    ],
    "responses": {
      "200": {
        "description": "Successfully uploaded document file"
      },
      "other": {
        "description": "Operation failed"
      }
    }
  }
}


JSON to Document Generation

Now we have our template ready. Let's generate the document.

Document generation has two phases.

  1. .Request to generate the document
  2. .Download the document

Step 1: Request To Generate the Document

We request document generation with the details required and we get an acknowledgment. The process happens behind the screen asynchronously.

Parameters Needed for the Document Generation

API: POST - /api/v1/document/generate/bulk  

Request Body

Form Data

documentId id of the template
format pdf/docx (Format should be supported by the template)
outputFileName The file name for the output file.
inputFile The file contains marker values. json, xlsx and xml supported.

Header

Content-Type multipart/form-data
x-access-token JWT auth token from login

Input Data

The data in the inputFile should be in a structure defined by the template. For example, For the above template mapping will be like the below.

  • Invoice_Number in JSON should be matching with {Invoice_Number} in the template. 
  • For table data, it should be an array of objects, with Item_Description and Amount.
  • The amount should be a number for summation calculation.

invoice

Step 2: Download the Document

The generated document can be downloaded using the output ID obtained from the above step and the name of the output file. 

We will use two APIs here. 

  1. API to know the file's existence : /api/v1/output/name/${fileName}
  2. API to download the file : /api/v1/output/download/${outputId}

Since document generation happens asynchronously, to know whether the document is generated or not, we will use the /api/v1/output/name api.

On the success response from API /api/v1/output/name will download the file.

I have combined both of these steps in a single js file and it looks below.

Java
 
let login = require("../edocgen_login");
const fs = require("fs");
const uuid = require("uuid");
const FormData = require("form-data");
let axios = require("axios");
let fileName = uuid.v4();
const headers = {
  "Content-Type": "multipart/form-data",
  "x-access-token": "null",
};

const hostName = "https://app.edocgen.com/api/v1/document/generate/bulk";


const outputFormat = "<format>";// pdf / docx
const documentId = "<template_id>";    // id of the template we want to use

module.exports.generateFiles =  function () {
  let authToken = login.getToken(function handleUsersList(token) {

    headers["x-access-token"] = token;

    var formBody = new FormData();
    formBody.append("documentId", documentId);
    formBody.append("format", outputFormat);
    formBody.append("outputFileName", fileName);
    // json data for the template
    formBody.append("inputFile", fs.createReadStream("./JSON_Data_Single.json"));   // local path forjson file

    let config = {
      method: "post",
      url: hostName,
      headers: headers,
      data: formBody,
    };

    console.log(`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`);
    let config_output = {
      method: "get",
      url:`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`,
      headers: headers,
    };

    const MAX_RETRY = 50;
    let currentRetry = 0;

    // max retry for 50 times
    function errorHandler() {
      if (currentRetry < MAX_RETRY) {
        currentRetry++;
        console.log("Document is not prepared yet! Retrying...");
        sendWithRetry(processResponse);
      } else {
        console.log("No luck. Document is not generated. Retried multiple times.");
      }
    }
    
    // sendWithRetry checks for file existence
    // on success, it proceeds to download the file
    // on failure, it retries 
    // todo: introduce spin lock
    function sendWithRetry(callback) {
      axios(config_output)
        .then(function (response) {
          if (response.data.output.length !== 1) {
            throw new axios.Cancel("Document is not found. Throw error.");
          } else {
            callback(response);
          }
        })
        .catch(errorHandler);
    }

    axios(config)
      .then(function (response) {
        sendWithRetry(processResponse);
      })
      .catch(function (error) {
        console.log(error);
      });
  });
};

function processResponse(response) {
  const outputId = response.data.output[0]._id;
  console.log(
    "Output Document is Generated. Id = ",
    response.data.output[0]._id
  );

  let config_download = {
    method: "get",
    url: `https://app.edocgen.com/api/v1/output/download/${outputId}`,
    headers: headers,
    responseType: "arraybuffer",

  };

  axios(config_download)
    .then(function (response) {
      console.log("Output file is downloaded " + `${fileName}.${outputFormat}`);
      fs.writeFileSync(`./${fileName}.${outputFormat}`, response.data);
    })
    .catch(function (error) {
      console.log("Error while downloading");
      console.log(error);
    });
}


Single vs Multiple Documents

When the data is single JSON, a single document of the given format will be generated.

When the data is an array of objects, documents for each array element will be generated and zipped into a file.

XML to Document Generation

The procedure is simple for XML data. All we need to do is pass the XML file in the place of JSON data. 

Like JSON to document, For XML to Document as well, we need documentId, outputFileName, format and inputFile. Everything same as JSON, except the input file, will be an XML file.

The sample XML data would look like below

XML
 
<?xml version="1.0" encoding="UTF-8" ?>
<marker>
  <values>
    <Invoice_Number>SBU-2053501</Invoice_Number>
    <Invoice_Date>31-07-2020</Invoice_Date>
    <Terms_Payment>Net 15</Terms_Payment>
    <Company_Name>ABC company</Company_Name>
    <Billing_Contact>ABC-Contact1</Billing_Contact>
    <Address>New york, United State</Address>
    <Email>support@edocgen.com</Email>
	<Logo>621cd2b783a6095d7b15a443</Logo> 
     <Sum1>6,751</Sum1>
	 <para>61b334ee7c00363e11da3439</para>
    <ITH>
      <Heading1>Item Description</Heading1>
      <Heading2>Amount</Heading2>
    </ITH>
    <IT>
      <Item_Description>Product Fees: X</Item_Description>
      <Amount>5,000</Amount>
    </IT>
  </values>
<marker>


Code change that I made for XML as the data source is simple as below

JavaScript
 
var formBody = new FormData();
formBody.append("documentId", documentId);
formBody.append("format", outputFormat);
formBody.append("outputFileName", fileName);
formBody.append("inputFile", fs.createReadStream("./XML_Invoice.xml"));


Database to Document Generation

Document generation from Databases is almost the same as other data sources. But in this case, instead of uploading the inputFile, we require to provide the connection details and SQL query.

The output columns of the SQL query should be matching with the tags in the document template.

Let's look at how to configure this in code.

JavaScript
 
const templateId = "<template id>";
const dbVendor = "mysql";
const dbUrl = "<jdbc connection URL>";
const dbLimit = "100";
const dbPassword = "<database password>";
const dbQuery = "SELECT JSON_ARRAY(first, last) FROM customers;";
const outputFormat = "pdf";

// form data prepareation
let formBody = new FormData();
formBody.append("documentId", templateId);
formBody.append("format", outputFormat);
formBody.append("dbVendor", dbVendor);
formBody.append("dbUrl", dbUrl);
formBody.append("dbLimit", dbLimit);
formBody.append("dbPassword", dbPassword);
formBody.append("dbQuery", dbQuery);
formBody.append("outputFileName", fileName);


Everything else would remain the same.

Sending the Document Over Email

eDocGen provides the facility to send the generated document over Email.

Parameters Needed for the Document Generation

API: POST - /api/v1/output/email 

Request Body 

JSON

outId

Put output ID here which needs to be sent via email

emailId Put user email here

Header

Content-Type multipart/form-data
x-access-token JWT auth token from login

Code Sample

 
let login = require("../edocgen_login");
let axios = require("axios");
const hostName = "https://app.edocgen.com/api/v1/output/email";
const headers = {
  "Content-Type": "application/json",
  "x-access-token": "null",
};

const outId = "<output ID>"; // Put output ID here which need to be sent via email
const emailId = "<user email>"; // Put user email here

module.exports.generateFiles = function () {
  let authToken = login.getToken(function handleUsersList(token) {
    headers["x-access-token"] = token;

    let payload = { outId: outId, emailId: emailId };
    let config = {
      method: "post",
      url: hostName,
      headers: headers,
      data: payload,
    };

    axios(config)
      .then(function (response) {
        console.log("Mail sent");
      })
      .catch(function (error) {
        console.log(error);
      });
  });
};


The email from eDocGen would look like below:

email from eDocGen

There are tons of other features that I couldn't cover here. But I hope this article could provide you with an idea of where to start.

Will meet you with another exciting article. Have a good day!

Document JavaScript PDF

Opinions expressed by DZone contributors are their own.

Related

  • Essential Security Measures for PDF Documents
  • Building a PDF Viewer From Scratch
  • Google Cloud Document AI Basics
  • Thumbnail Generator Microservice for PDF in Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!