Generating PDF files from HTML with inserted business data (integrated with DocRaptor)

DocRaptor is a web service that converts source HTML data received via API into PDF format and returns it as a PDF file. Questetra provides a built-in automated step that integrates with DocRaptor to generate PDF files.

This article explains the basic usage of the built-in automated step “DocRaptor: Generate PDF“.

A similar automated step for generating PDF files, [Adobe: PDF Generation (HTML to PDF)], which accesses the Adobe Acrobat Service API, is also available.

Preparation for DocRaptor

Create an account on the DocRaptor website (English only). After entering your email address, name, and password and clicking [Sign Up], an API key will be issued and displayed on the “Document History” page. The API key is used in the HTTP authentication settings for [DocRaptor: Generate PDF].

DocRaptor free accounts can generate up to 5 documents per month. When a Workflow App is run in debug mode, PDFs are generated in DocRaptor’s TEST mode. PDFs generated in TEST mode include a DocRaptor watermark but are not counted toward the generation limit.

Preparing the Source HTML with Inserted Business Data

In this article, we refer to the HTML with SpEL expressions written in the places where business data is to be inserted as the “source HTML”.

When the source HTML is output by the automated step [Generate Text File], the SpEL expressions are replaced with the values of Data Items, resulting in an HTML file with business data inserted. This HTML file is then converted to PDF by [DocRaptor: Generate PDF].

For SpEL expression syntax and available functions, see R2272: Output of Strings via EL syntax (Data Setting Expression).

Sample App

DocRaptor test app

In the sample app, the values entered in the “Enter Data” step are inserted into the source HTML by the “Generate HTML File” step ([Generate Text File]) and output as an HTML file, and then the “Generate PDF” step ([DocRaptor: Generate PDF]) converts the HTML file to PDF.

The source HTML is based on the A4 size template for invoices (invoice.A4.html) from DocRaptor’s Free HTML to PDF Templates. Save the file linked at Download A4 Size.

App Settings

Data Items

The Data Items are as follows. All items except “HTML File” and “PDF File” are Data Items to be inserted into the source HTML.

Data Item NameData TypeField NameDisplay in “Enter Data” stepDescription
HTML FileFileq_htmlNo DisplayStores the HTML file output by the “Generate HTML File” step
PDF FileFileq_pdfNo DisplayStores the PDF file generated by the “Generate PDF” step
Billing nameString (single line)q_billing_nameEditable
Billing addressString (multiple lines)q_billing_addressEditable
Billing email addressString (single line)q_billing_emailEditable
Payment due dateDate (Y/M/D)q_due_dateEditableInitial Value: #today.addDays(30)
Item name 1String (single line)q_item_name1Editable
Remark 1String (single line)q_remark1Editable
Unit price 1Numericq_unit1Editable
Quantity 1Numericq_quantity1Editable
Price 1Numericq_price1EditableExpression: #q_unit1 * #q_quantity1
Item name 2String (single line)q_item_name2Editable
Remark 2String (single line)q_remark2Editable
Unit price 2Numericq_unit2Editable
Quantity 2Numericq_quantity2Editable
Price 2Numericq_price2EditableExpression: #q_unit2 * #q_quantity2
Item name 3String (single line)q_item_name3Editable
Remark 3String (single line)q_remark3Editable
Unit price 3Numericq_unit3Editable
Quantity 3Numericq_quantity3Editable
Price 3Numericq_price3EditableExpression: #q_unit3 * #q_quantity3
SubtotalNumericq_subtotalEditableExpression: #q_price1 + #q_price2 + #q_price3
TaxNumericq_taxEditableExpression: #q_subtotal * 0.1
Total amountNumericq_totalEditableExpression: #q_subtotal + #q_tax
Issue dateDate (Y/M/D)q_issue_dateEditableInitial Value: #today
Invoice IDString (single line)q_invoice_idEditableInitial Value: #{processInstanceSequenceNumber}

“Generate HTML File” Step (Generate Text File)

This step replaces the SpEL expressions in the source HTML registered in “C4: Contents of text file” with the values of Data Items and saves the result as an HTML file in the Data Item “HTML File.” The settings for the sample app are as follows.

Setting ItemSample App Setting
C1: File-type data item to save text file“HTML File”
C3: Saving file namesource-#{processInstanceId}.html (the file extension must be .html)
C4: Contents of text fileSource HTML (created in “Editing the Template”)
C6: File type when savingtext/html

“Generate PDF” Step (DocRaptor: Generate PDF)

This step sends the HTML file in the Data Item “HTML File” to DocRaptor and saves the generated PDF file in the Data Item “PDF File.” First, register the HTTP authentication settings for accessing the DocRaptor API.

On the “Generate PDF” step settings screen, click [Set up Setting] next to “C1: BASIC Authentication Setting in which API Key is set as Username” to open the “HTTP Authentication Settings” screen. Open the [Basic Authentication] tab at the top and click [+ Add] under “Workflow app specific settings.” Enter any name (e.g., “DocRaptor”) in “Name,” enter the DocRaptor API key in “Username,” leave “Password” blank, and click [Save].

The settings for the sample app are as follows.

Setting ItemSample App Setting
C1: BASIC Authentication Setting in which API Key is set as UsernameThe name of the registered HTTP authentication setting (e.g., “DocRaptor”)
C2: Data item that stores the source HTML“HTML File”
C3: Data item to save the generated PDF file“PDF File”
C5: File name to save as#{processInstanceId}.pdf (the file extension must be .pdf)

If the registered name does not appear in the selection list for “C1,” reload the settings screen.

Editing the Template

Rewrite the template (invoice.A4.html) to create the source HTML. Proceed by generating a PDF to verify each part as you edit it. The procedure for generating a PDF is described in “Running [DocRaptor: Generate PDF].”

Checking the Template

First, convert the template to PDF as-is. On the settings screen of the “Generate PDF” step, click [Debug only this step]. In the “Input test data” step of the started debug Case, [Add] invoice.A4.html directly to the Data Item “HTML File,” and click [Finish “Input test data”]. After the debug Case completes, open the PDF file in the Data Item “PDF File.”

In the generated PDF, the company name and email address contain placeholder strings. The areas surrounded by green rectangles in the figure below are fixed strings common to all invoices — rewrite them with appropriate text such as your company name. The areas surrounded by red rectangles are strings that vary per invoice — rewrite them with SpEL expressions so that Data Item values are inserted.

Rewriting Fixed Strings

Open invoice.A4.html in a text editor. The first part of the code is a style sheet (<style>–</style>) that specifies the layout. The text that appears in the PDF is found after <header>. First, rewrite the “Logo & Name” and “Invoice #100” sections.

<div class="logoAndName">
  <svg>
    <circle cx="50%" cy="50%" r="40%" stroke="black" stroke-width="3" fill="black" />
  </svg>
  <h1>Logo & Name</h1>
</div>
<!-- Details about the invoice are on the right top side of each page. -->
<div class="invoiceDetails">
  <h2>Invoice #100</h2>
  <p>
    07 March 2021
  </p>
</div>

Replace <h1>Logo & Name</h1> with the company name (Questetra), and delete <svg>–</svg> (the logo image).

<div class="logoAndName">
  <h1>Questetra</h1>
</div>
<!-- Details about the invoice are on the right top side of each page. -->
<div class="invoiceDetails">
  <h2>Invoice #100</h2>
  <p>
    07 March 2021
  </p>
</div>

After rewriting, paste the entire code into “C4: Contents of text file” of the “Generate HTML File” step, and verify the PDF using the procedure described in “Running [DocRaptor: Generate PDF].” Continue verifying the PDF each time you edit a section.

Rewriting with SpEL Expressions

Next, rewrite the parts that vary per invoice with SpEL expressions that reference Data Items. Data Item values are referenced with #{#fieldName}. For String-type Data Items, use HTML escaping such as #{#escaper.escapeHtml(#fieldName)} before inserting. This ensures that if the input contains < or &, the HTML structure is not broken. In the code rewritten above, replace “#100” with an expression referencing “Invoice ID” (q_invoice_id), and “07 March 2021” with an expression referencing “Issue date” (q_issue_date).

<div class="logoAndName">
  <h1>Questetra</h1>
</div>
<!-- Details about the invoice are on the right top side of each page. -->
<div class="invoiceDetails">
  <h2>Invoice ##{#escaper.escapeHtml(#q_invoice_id)}</h2>
  <p>
    #{#q_issue_date}
  </p>
</div>

You can use functions to change the output format. The following code rewrites the “Invoice to” and “Due Date” sections to insert the billing details and payment due date Data Items. The “Billing address” (String (multiple lines)) and “Payment due date” (Date (Y/M/D)) use functions to change the output format.

<div>
  <h3>Invoice to</h3>
  <p>
    <b>#{#escaper.escapeHtml(#q_billing_name)}</b>
    <br />
    #{#joiner.splitJoin(#escaper.escapeHtml(#q_billing_address), '<br>')}
    <br />
    <a href="mailto:#{#escaper.escapeHtml(#q_billing_email)}">
      #{#escaper.escapeHtml(#q_billing_email)}
    </a>
  </p>
</div>
<!-- Additional details can be placed below the invoice details. -->
<div>
  <h3>Due Date</h3>
  <p>
    <b>#{#dateFormatter.format('dd MM yyyy', #q_due_date)}</b>
  </p>

If the value stored in a String (multiple lines) Data Item is inserted as-is with #{#q_billing_address}, the text will not break in HTML and will appear on a single line. Write a function that inserts a <br> for each line to preserve the multiple lines.

Running [DocRaptor: Generate PDF]

Generated PDF

Run [Debug execution] from the Start Event and process the “Enter Data” step. The “Generate HTML File” step and the “Generate PDF” step will execute consecutively. On the detail screen of the completed debug Case, open the PDF file in the Data Item “PDF File.” Since this is a debug execution, the PDF is generated in DocRaptor’s TEST mode (see “Preparation for DocRaptor”).

Once the entire template has been rewritten, verify that the values entered in the “Enter Data” step are inserted into the PDF file. If the PDF is not generated as expected, review the source HTML in “C4: Contents of text file” of the “Generate HTML File” step.

HTML Code Sample

The full source HTML created by rewriting the template (invoice.A4.html) is shown below (click to expand).

HTML Code (Full Text)
<!--
The following is the license for the original template file.
------------------------------------
MIT License
Copyright (c) 2021 Expected Behavior, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------
-->
<style>
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap');
:root{
  --font-color: black;
  --highlight-color: #60D0E4;
  --header-bg-color: #B8E6F1;
  --footer-bg-color: #BFC0C3;
  --table-row-separator-color: #BFC0C3;
}
@page{
  size:A4;
  margin:8cm 0 3cm 0;
  @top-left{
    content:element(header);
  }
  @bottom-left{
    content:element(footer);
  }
}
body{
  margin:0;
  padding:1cm 2cm;
  color:var(--font-color);
  font-family: 'Montserrat', sans-serif;
  font-size:10pt;
}
a{
  color:inherit;
  text-decoration:none;
}
hr{
  margin:1cm 0;
  height:0;
  border:0;
  border-top:1mm solid var(--highlight-color);
}
header{
  height:8cm;
  padding:0 2cm;
  position:running(header);
  background-color:var(--header-bg-color);
}
header .headerSection{
  display:flex;
  justify-content:space-between;
}
header .headerSection:first-child{
  padding-top:.5cm;
}
header .headerSection:last-child{
  padding-bottom:.5cm;
}
header .headerSection div:last-child{
  width:35%;
}
header .logoAndName{
  display:flex;
  align-items:center;
  justify-content:space-between;
}
header .logoAndName svg{
  width:1.5cm;
  height:1.5cm;
  margin-right:.5cm;
}
header .headerSection .invoiceDetails{
  padding-top:.5cm;
}
header .headerSection h3{
  margin:0 .75cm 0 0;
  color:var(--highlight-color);
}
header .headerSection div:last-of-type h3:last-of-type{
  margin-top:.5cm;
}
header .headerSection div p{
  margin-top:2px;
}
header h1,
header h2,
header h3,
header p{
  margin:0;
}
header .invoiceDetails,
header .invoiceDetails h2{
  text-align:right;
  font-size:1em;
  text-transform:none;
}
header h2,
header h3{
  text-transform:uppercase;
}
header hr{
  margin:1cm 0 .5cm 0;
}
main table{
  width:100%;
  border-collapse:collapse;
}
main table thead th{
  height:1cm;
  color:var(--highlight-color);
}
main table thead th:nth-of-type(2),
main table thead th:nth-of-type(3),
main table thead th:last-of-type{
  width:2.5cm;
}
main table tbody td{
  padding:2mm 0;
}
main table thead th:last-of-type,
main table tbody td:last-of-type{
  text-align:right;
}
main table th{
  text-align:left;
}
main table.summary{
  width:calc(40% + 2cm);
  margin-left:60%;
  margin-top:.5cm;
}
main table.summary tr.total{
  font-weight:bold;
  background-color:var(--highlight-color);
}
main table.summary th{
  padding:4mm 0 4mm 1cm;
}
main table.summary td{
  padding:4mm 2cm 4mm 0;
  border-bottom:0;
}
aside{
  -prince-float: bottom;
  padding:0 2cm .5cm 2cm;
}
aside > div{
  display:flex;
  justify-content:space-between;
}
aside > div > div{
  width:45%;
}
aside > div > div ul{
  list-style-type:none;
  margin:0;
}
footer{
  height:3cm;
  line-height:3cm;
  padding:0 2cm;
  position:running(footer);
  background-color:var(--footer-bg-color);
  font-size:8pt;
  display:flex;
  align-items:baseline;
  justify-content:space-between;
}
footer a:first-child{
  font-weight:bold;
}
</style>
<header>
  <div class="headerSection">
    <div class="logoAndName">
      <h1>Questetra</h1>
    </div>
    <div class="invoiceDetails">
      <h2>Invoice ##{#escaper.escapeHtml(#q_invoice_id)}</h2>
      <p>
        #{#q_issue_date}
      </p>
    </div>
  </div>
  <hr />
  <div class="headerSection">
    <div>
      <h3>Invoice to</h3>
      <p>
        <b>#{#escaper.escapeHtml(#q_billing_name)}</b>
        <br />
        #{#joiner.splitJoin(#escaper.escapeHtml(#q_billing_address), '<br>')}
        <br />
        <a href="mailto:#{#escaper.escapeHtml(#q_billing_email)}">
          #{#escaper.escapeHtml(#q_billing_email)}
        </a>
      </p>
    </div>
    <div>
      <h3>Due Date</h3>
      <p>
        <b>#{#dateFormatter.format('dd MM yyyy', #q_due_date)}</b>
      </p>
      <h3>Amount</h3>
      <p>
        <b>¥#{#q_total}</b>
      </p>
    </div>
  </div>
</header>
<footer>
    <a href="https://questetra.com">
      questetra.com
    </a>
    <a href="mailto:customer-sevice@questetra.com">
      customer-sevice@questetra.com
    </a>
    <span>
      206 Takamiya-cho Oike Bldg. 4th Fl. Nakagyo-ku Kyoto 604-0835 JAPAN
    </span>
</footer>
<main>
  <table>
    <thead>
      <tr>
        <th>Item Description</th>
        <th>Rate</th>
        <th>Amount</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>
          <b>#{#escaper.escapeHtml(#q_item_name1)}</b>
          <br />
          #{#escaper.escapeHtml(#q_remark1)}
        </td>
        <td>
          #{#q_unit1}
        </td>
        <td>
          #{#q_quantity1}
        </td>
        <td>
          #{#q_price1}
        </td>
      </tr>
      <tr>
        <td>
          <b>#{#escaper.escapeHtml(#q_item_name2)}</b>
          <br />
          #{#escaper.escapeHtml(#q_remark2)}
        </td>
        <td>
          #{#q_unit2}
        </td>
        <td>
          #{#q_quantity2}
        </td>
        <td>
          #{#q_price2}
        </td>
      </tr>
      <tr>
        <td>
          <b>#{#escaper.escapeHtml(#q_item_name3)}</b>
          <br />
          #{#escaper.escapeHtml(#q_remark3)}
        </td>
        <td>
          #{#q_unit3}
        </td>
        <td>
          #{#q_quantity3}
        </td>
        <td>
          #{#q_price3}
        </td>
      </tr>
    </tbody>
  </table>
  <table class="summary">
    <tr>
      <th>
        Subtotal
      </th>
      <td>
        #{#q_subtotal}
      </td>
    </tr>
    <tr>
      <th>
        Tax
      </th>
      <td>
        #{#q_tax}
      </td>
    </tr>
    <tr class="total">
      <th>
        Total
      </th>
      <td>
        #{#q_total}
      </td>
    </tr>
  </table>
</main>
<aside>
  <hr />
  <div>
    <div>
      <b>Terms & Conditions</b>
      <p>
        Please make payment within 30 days of issue of the invoice.
      </p>
    </div>
    <div>
      <b>Payment Options</b>
      <ul>
        <li>Paypal</li>
        <li>Credit Card</li>
      </ul>
    </div>
  </div>
</aside>

Discover more from Questetra Support

Subscribe now to keep reading and get access to the full archive.

Continue reading