Generating a PDF from HTML - ServiceNow Community
tim2222
Tera Expert

This is a brief guide to using the GeneralFormAPI Script Include to generate a PDF from an HTML source. There are a few utility classes for generating PDFs but ultimately they all end up in GeneralFormJava, which is a set of wrapper classes for iText 5.5.x. The limitations to what we can do with PDF are imposed by those wrapper classes.

See also https://docs.servicenow.com/bundle/newyork-hr-service-delivery/page/product/human-resources/task/gen...

Some things to consider:

  • Only supports generating a PDF into a new attachment, so you will need a record to send the output to.
  • A limited set of HTML and styling is supported.
  • A single image can be used in the page header.
  • A single image or plain text annotation can be used in the page footer.
  • Images need to be in (?) png format and are supplied using a data URI (i.e. base64 encoded).
  • You cannot use <hr> in your HTML: if you're using data from HTML fields make sure you filter out <hr> before passing to createPDF.
  • I haven't tested SVG (see createPDF in GeneralFormAPI).
  • Use HTML tables to achieve layout (but using width= will break)
  • For more limitations see https://hi.service-now.com/kb_view.do?sysparm_article=KB0693303

The following is a complete example that should work when executed as a background script. Only do this in a sub-production instance!

(function() {
	
// grab a random RITM	
var ritm = new GlideRecord('sc_req_item');
ritm.setLimit(1);
ritm.query();
ritm.next();
var filename = ritm.number + '.pdf';

gs.debug('Writing PDF to ' + ritm.number);

var table = ritm.getTableName();
var table_sys_id = ritm.getUniqueValue();

// create a new PDF generator
var formAPI = new global.GeneralFormAPI(filename, table, table_sys_id);

// grab a random image
var headerImage = null;
var att = new GlideRecord('sys_attachment');
att.addQuery('content_type', 'image/png');
att.addEncodedQuery('table_nameISNOTEMPTY');
att.query();
if (att.next()) {
	headerImage = getAttachmentBase64(att);
}

var footer = 'Your footer message here';

// setDocument(header image, footer image, footer text, header alignment, footer alignment, paper size)
// alignment = "0" = left, "1" = centre, "2" = right. An invalid alignment will cause the image to not appear
formAPI.setDocument(headerImage, null, footer, '1', '1', 'a4');

// source HTML
// must be well-formed XML and not use <hr> (really)
var pages = [
	{heading: '<h1>My First PDF</h1><p>First page content</p>' },
	{heading: '<p color="red">Second page content</p>' },
	{heading: '<table border="1"><tr bgcolor="pink"><td>Pink rocks!</td><td align="center">Center</td></tr><tr><td colspan="2" align="center">Second row</td></tr></table>' },
	{heading: '<img src="' + getAttachmentBase64(att) + '"/>'}
];

gs.debug('Create the PDF!');

// createPDF(html, pages)
// if you don't want to use pages just supply the html argument
formAPI.createPDF('', pages);

function getAttachmentBase64(attachmentGR) {
	var base64ImageStr = GlideStringUtil.base64Encode(new GlideSysAttachment().getBytes(attachmentGR));
	return "data:image/png;base64," + base64ImageStr + "";
};


})();
Comments
Michael Jones -
Giga Sage

That's a nifty little concept; thank you for sharing. Might be a useful way to generate forms on the fly where a customer still has "print, sign and file" as part of a process (with some work of course). 

elanabogdansky
Kilo Explorer

Tim- this is great! thank you so much for sharing

DaSmith9
Tera Expert

Thank you Tim, this was helpful.

This was more or less what I was wanting to do, except for the random RITM and Header image used in the example. I modified slightly, and am going to use this in a UI action to place the PDF back onto the record where the UI action was initiated from. I could see where this could also be used on a Bus Rule or even a Scheduled Job depending on when you wanted to trigger the PDF.

I've got the base PDF created and attaching to the record I want via UI Action, now it's just a matter of finalizing the formatting/content within the body or pages. Having it on the UI Action allows for you to use 'current' and easily place data from within the record into the PDF which is also what I am looking to do.

Thanks again. I saw several other posts and reviewed the official ServiceNow documentation, but this example helped me really apply all that information. Good job.

Thanks,

Daniel

godfrey1
Kilo Contributor

Hi Daniel,

We are doing similar practice and trying to use business rule or UI action to trigger the PDF and attach to the case record.

Would you please share how can we create the base PDF and attach to the record?

Thank you,

Godfrey

DaSmith9
Tera Expert

Hi Godfrey, I started by creating a UI Action for proof of concept and copying in the script above that Tim provided, then I modified as needed for our requirements. You should do the same to start with if you haven't already. Play around with the content section as well to learn the different configuration points. 

For using in a UI Action, I modified the top section to query the current record, as opposed to the example above which is querying a random RITM record, as this is where I wanted the PDF to get attached. The variables 'filename', 'table' and 'target_sys_id' are then used as parameters in the PDF API call. 

I also added a date stamp and suffix to the filename string, in case the UI action was run more than once it might help differentiate when it was created by just looking at the filename. 

// get a glide record for current record	
    var gr = new GlideRecord('table_name_goes_here');
    gr.addQuery('sys_id', current.sys_id);
    gr.query();
    gr.next();

    //set filename
    var filename = gs.now() + '_' + gr.number + '_filename_suffix_goes_here.pdf';

    //get table and target record glide records
    var table = gr.getTableName();
    var target_sys_id = gr.getUniqueValue();

One other thing I ran into, was the greater than '>' and less than '<' characters in a string field. Because HTML tagging uses the < & > to create the formatting tags, this can cause a little havoc and potentially cause a PDF page not to generate. 

There was a single < symbol that was coming across in one of our string fields that broke things (a comment field). I had to strip out the < from the string prior to using in the source HTML section, otherwise it was interpreted as a broken HTML tag. With the < in the string, our second PDF page was generating blank. 

Good luck with it,

Daniel

DaSmith9
Tera Expert

Hi Godfrey, I started by creating a UI Action for proof of concept and copying in the script above that Tim provided, then I modified as needed for our requirements. You should do the same to start with if you haven't already. Play around with the content section as well to learn the different configuration points. 

For using in a UI Action, I modified the top section to query the current record, as opposed to the example above which is querying a random RITM record, as this is where I wanted the PDF to get attached. The variables 'filename', 'table' and 'target_sys_id' are then used as parameters in the PDF API call. 

I also added a date stamp and suffix to the filename string, in case the UI action was run more than once it might help differentiate when it was created by just looking at the filename. 

// get a glide record for current record	
    var gr = new GlideRecord('table_name_goes_here');
    gr.addQuery('sys_id', current.sys_id);
    gr.query();
    gr.next();

    //set filename
    var filename = gs.now() + '_' + gr.number + '_filename_suffix_goes_here.pdf';

    //get table and target record glide records
    var table = gr.getTableName();
    var target_sys_id = gr.getUniqueValue();

One other thing I ran into, was the greater than '>' and less than '<' characters in a string field. Because HTML tagging uses the < & > to create the formatting tags, this can cause a little havoc and potentially cause a PDF page not to generate. 

There was a single < character that was coming across in one of our string fields that broke things (a comment field). I had to strip out the < from the string prior to using in the source HTML section, otherwise it was interpreted as a broken HTML tag. With the < in the string, our second PDF page was generating blank. 

Good luck with it,

Daniel

Victor23
Tera Contributor

Hello,

 

I was looking for this and it worked for me,

 

Thank you very much!

Victor.

Smith Johnson
Tera Guru

@tim2222 Hello,

is there any option to get the sys_id of the new attachment after the "formAPI.createPDF('', pages);" ??

tim2222
Tera Expert

@Smith Johnson , inspecting the global.GeneralFormAPI script include the "createPDF" function doesn't return any value so you can't get the sys_id directly.

So you would have to search the attachments table using the filename and record id you used. If you needed to be certain you got the right file, you could use a GUID as the filename.

yash18
Kilo Contributor

Hi,

 

How can i dynamically add my fields values like you are using MY page or my second page i want to fetch it from form and then use it.

 

Thanks

Ranjan10
Giga Explorer

var pages = [
{heading: '<h3 background-color:grey;>Application Form For Return Home Transportation Expanses</h3><br><br><table border="1"><tr><td>DATE</td><td>'+current.variables.date.getDisplayValue()+'</td><td>Name</td><td>'+u_name+'</td></tr><tr><td>Department</td><td>'+current.variables.reading.getDisplayValue()+'</td><td>GID</td><td>'+current.variables.gid.getDisplayValue()+'</td></tr></table><br><br><table border="1"><tr><td>Number of Application</td><td>YEAR(Prefecture)</td><td>Address</td><td>Route Name</td><td>station name</td></tr>'+ak()+'</table><br><br><table border="1"><tr><td>FullName</td><td>relationship</td><td>address</td><td>Route Name</td><td>station_name</td></tr>'+gk()+'</table><br><br><table border="1"><tr><td>Date</td><td>Trip</td><td>From</td><td>To</td><td>User</td><td>Transportation</td><td>Line</td><td>Receipts no.</td><td>Amount</td></tr>'+rk()+'</table>'}

]; 

 

hello Sir, 

how can we add css inside this html tag. I used "background: grey" but not working 

Ranjan10
Giga Explorer

Hello sir, 

how can we add Css in the above code, i have try but not working

rahul Biradar
Kilo Explorer

Where the PDF saved ? i have run this script but i am not able to find out the pdf which is generated 

Nagashree5
Tera Contributor

Hi @DaSmith9 

I have a similar requirement. I'm very new to the PDF generating thing in ServiceNow. 

Can you please have a look at my requirement.

Requirement : Once a request is submitted through the portal, the form-level variables must be automatically populated in to a customized template and attach to the submitted request.

Suppose the form has three variables - Requested for, Company and Date. Once, these fields are filled and form is submitted, these variables should be populated in a custom pdf like below.

Dear (Requested for),

I'm working for (Company) till (Date)

It should automatically fill these details based on the request and get attached to post submission.

Is this feasible in Servicenow.

Please guide me on this.

Thank you in Advance.

Nagashree5
Tera Contributor

Hi @tim2222 , @DaSmith9 

I have a similar requirement. I'm very new to the PDF generating thing in ServiceNow. 

Can you please have a look at my requirement.

Requirement : Once a request is submitted through the portal, the form-level variables must be automatically populated in to a customized template and attach to the submitted request.

Suppose the form has three variables - Requested for, Company and Date. Once, these fields are filled and form is submitted, these variables should be populated in a custom pdf like below.

Dear (Requested for),

I'm working for (Company) till (Date)

It should automatically fill these details based on the request and get attached to post submission.

Is this feasible in ServiceNow.

Could you please guide me on this.

Thank you in Advance.

Version history
Last update:
‎02-19-2020 08:25 AM
Updated by: