logo

NJP

Generating Custom PDFs - using the new PDFGenerationAPI

Import · Feb 18, 2021 · article

ServiceNow recently released a new plugin for the platform (Paris onwards) to enable better support for custom PDF generation using script. Unfortunately the only documentation on this new feature set you will find is in KB0866613, which does not go very far in explaining how to use the API. I'm sure ServiceNow will eventually document this, or even build some full featured platform functionality around it, but for now I hope this article will get you started.

The new API appears to be based on iText7, which is a major improvement over the old GeneralPDF that was based on iText5. The newer version of iText is much better at handling HTML conversion, and once you start to explore the feature set, there is quite a lot you can do with just plain HTML and CSS.

API Overview

At the time of writing this article, there are two known methods available on the API.

convertToPDFWithHeaderFooter

Parameter Type Description
html String The HTML content to convert to a PDF
targetTable String The table name to attach the generated PDF to
targetSysId String sys_id of the record to attach the generated PDF to
filename String The file to to save the generated PDF as
pageProperties Object Javascript object with properties used during PDF generation
fontSysId String sys_id of a font family record on the 'sys_pdf_generation_font_family' table

pageProperties

property Type Description
HeaderImageAttachmentId String sys_id of an attachment record
HeaderImageAlignment String One of: 'LEFT', 'CENTER', 'RIGHT'
PageSize String One of: 'LEGAL', 'LETTER', 'A4'
GeneratePageNumber String One of: 'true', 'false'
TopOrBottomMargin String Number as a string e.g. '72'
LeftOrRightMargin String Number as a string e.g. '36'

Example

var pageProperties = {
    HeaderImageAttachmentId: 'attachment_sys_id',
    HeaderImageAlignment: 'LEFT',
    PageSize: 'A4',
    GeneratePageNumber: 'true',
    TopOrBottomMargin: '72',
    LeftOrRightMargin: '36'
};

var html = '<h1>Hello World</h1>';

new sn_pdfgeneratorutils.PDFGenerationAPI().convertToPDFWithHeaderFooter(html, 'incident', 'incident_sys_id', 'My First PDF', pageProperties, 'font_family_sys_id');

convertToPDF

Parameter Type Description
html String The HTML content to convert to a PDF
targetTable String The table name to attach the generated PDF to
targetSysId String sys_id of the record to attach the generated PDF to
filename String The file to to save the generated PDF as
fontSysId String sys_id of a font family record on the 'sys_pdf_generation_font_family' table

Example

var html = '<h1>Hello World</h1>';

new sn_pdfgeneratorutils.PDFGenerationAPI().convertToPDF(html, 'incident', 'incident_sys_id', 'My First PDF', 'font_family_sys_id');

On the face of it, it would appear as though the convertToPDFWithHeaderFooter is the more flexible of the two methods, however as you'll see below, we can actually control the entire document ourselves with HTML and CSS and just use the convertToPDF method.

CSS 3 Paged Media

The CSS 3 specification sets out functionality for paged media, for example printing. When it comes to PDF we can use the paged media rules to define how the document should be generated using the ServiceNow PDFGenerationAPI / iText7.

@page rule

The @page rule allows us to specify details about the pages we will be generating, including the margins, size, orientation, page numbering rules etc.

Example

<style>
   @page {
      size: A4 landscape;
      margin-left: 1cm;
      margin-top: 3cm;
      margin-right: 1cm;
      margin-bottom: 3cm;

      @bottom-right {
        font-family: sans-serif;
        font-weight: bold;
        font-size: 1em;
        content: counter(page);
      }
   }
</style>
<h1>
   Hello World!
</h1>

Resulting PDF

image

Headers and Footers

The creation of headers and footers is equally as easy. This uses a feature called 'Running elements'. At first they may not make much sense in how you define them, but see the below for an example.

<style>
   @page {
      size: A4 landscape;
      margin-left: 1cm;
      margin-top: 3cm;
      margin-right: 1cm;
      margin-bottom: 3cm;

      @top-center {
        font-family: sans-serif;
        font-weight: bold;
        font-size: 1em;
        content: element(runningPageHeader);
      }
   }

   #pageHeader {
      position: running(runningPageHeader)
   }
</style>

<div id="pageHeader">
   My Header!
</div>

<h1>
   Hello World!
</h1>

Resulting PDF

image

How this works

The div element with the id 'pageHeader' would normally be rendered in the position its included in the code on the page, however the style block of the code contains a CSS rule for the element #pageHeader, specifying position: running(runningPageHeader). This removes the element from the rendered page and adds it into a css variable, in our case the variable is called 'runningPageHeader'. Once the content is in the variable, we can use that same variable name to include it as content in the @top-center rule using content: element(runningPageHeader).

This can be a difficult concept to get your head around at first, and it can get more complicated when you want to include a page number in your custom HTML header content too. For example:

<style>
   @page {
      size: A4 landscape;
      margin-left: 1cm;
      margin-top: 3cm;
      margin-right: 1cm;
      margin-bottom: 3cm;

      @top-center {
        font-family: sans-serif;
        font-weight: bold;
        font-size: 1em;
        content: element(runningPageHeader);
      }
   }

   #pageHeader {
      position: running(runningPageHeader)
   }

   #currentPageNumber:before {
      content: counter(page)
   }
</style>

<div id="pageHeader">
   My Header! <span id="currentPageNumber"/>
</div>

<h1>
   Hello World!
</h1>

Resulting PDF

image

Images

Images are also possible with relative ease. You can use the usual img elements in your html, and even use absolute URLs to image content, be aware though that if the image is hosted on your ServiceNow instance, you need to make sure the image is publicly accessible. Attachments against records won't work unfortunately as the PDF generation API is not authenticated to your instance.

Example

<style>
   @page {
      size: A4 landscape;
      margin-left: 1cm;
      margin-top: 3cm;
      margin-right: 1cm;
      margin-bottom: 3cm;

      @top-center {
        font-family: sans-serif;
        font-weight: bold;
        font-size: 1em;
        content: element(runningPageHeader);
      }
   }

   #pageHeader {
      position: running(runningPageHeader)
   }

   #currentPageNumber:before {
      content: counter(page)
   }
</style>

<div id="pageHeader">
   My Header! <span id="currentPageNumber"/>
</div>

<h1>
   Hello World!
</h1>
<img src="URL_TO_IMAGE" width="100px"/>

Resulting PDF

image

Fonts

It is possible to add and use custom fonts into the PDFs that you generate. I've only tried this with TTF (TrueType) fonts, though it may work with other formats too. It's possible to add more than one font to a PDF, see the following example.

  1. Create a new Font Family [sys_pdf_generation_font_family] record
  2. fill in the name field with whatever you like, it doesn't make any difference to the process.
  3. Save the record
  4. Attach your font files to the record.

image

When using the API, the last parameter of both methods is the font family sys_id, you can only supply one sys_id to the method, which is why you add multiple fonts to the same Font Family record.

To use the font in your document, you simply specify the font-family CSS attribute in your CSS

Example

<style>
   @page {
      size: A4 landscape;
      margin-left: 1cm;
      margin-top: 3cm;
      margin-right: 1cm;
      margin-bottom: 3cm;

      @top-center {
        font-family: sans-serif;
        font-weight: bold;
        font-size: 1em;
        content: element(runningPageHeader);
      }
   }

   #pageHeader {
      position: running(runningPageHeader)
   }

   #currentPageNumber:before {
      content: counter(page)
   }

   .angry-birds-font {
      font-family: AngryBirds;
   }

   .flow-font {
      font-family: Flow;
   }
</style>

<div id="pageHeader">
   My Header! <span id="currentPageNumber"/>
</div>

<h1 class="flow-font">
   Hello World!
</h1>
<h1 class="angry-birds-font">
   Angry Birds
</h1>

Resulting PDF

image

Summary

Hopefully this article has helped you get to grips with the new PDFGenerationAPI. I have only scratched the surface of the CSS paged media rules, but a little Googling around should get you loads of information on how to use it.

Why not have a play with this on your developer instance and post some screenshots here to show everyone else what you've been able to achieve, I'd love to see some of what you've managed to create.

As always, if this content has helped you, please remember to mark it as helpful so that it can reach as many people as possible.

Callum

View original source

https://www.servicenow.com/community/developer-articles/generating-custom-pdfs-using-the-new-pdfgenerationapi/ta-p/2318979