The Now Platform® Washington DC release is live. Watch now!
on 07-18-2018 08:28 AM
This document covers the basics of each one as well as some technical details about script includes and other components that facilitate this functionality. We include links to official documentation when applicable.
The body text is defined and formatted with the TinyMCE editor and can merge or include user and table variables during generation. These variables can be selected and defined on the side of the editor.
Please keep in mind that some of the HTML elements you can create using the editor are not supported by the PDF generator at this time. See the "PDF functionality that is not supported below" for more information on what is and is not supported.
Please see HR Document Templates in our official documentation for more information.
The most common use case is allowing an HR agent to generate a document from a case via UI action (A good OOB example is Employment Verification Letter.) This requires the following steps:
The first bullet point is straightforward, however there are a couple of ways to call the document generation functionality. With HR installed, you can use our wrapper method to generate the PDF by creating an instance of our helper class and calling the generate method:
// tableName: Name of the table that the Document Template is based on.
// tableId: Sys ID of the record in the table to use for generation
// targetTableName: Name of the table to attach the generated PDF
// targetTableId: Sys ID of the record in the target table where the PDF will be attached
new GeneralHRForm(tableName, tableId, targetTableName, targetTableId).generate();
1. Create an instance of the GeneralFormAPI class
// fileName: Name of the PDF file that will be generated
// targetTable: Name of the table to attach the PDF
// targetTableSysID: Record to attach the PDF
new GeneralFormAPI(fileName, targetTable, targetTableSysId);
2. Set the values saved in the Document Template class
// header: Header image for the PDF
// footer: Footer image for the PDF
// headerLocation: Is the header right, left, or center aligned
// footerLocation: Is the footer right, left, or center aligned
GeneralFormAPI.SetDocument(header, footer, headerLocation, footerLocation);
3. Call the createPDF method and pass in the body text defined in the document template
// body: body text defined in the template
GeneralFormAPI.createPDF(body);
The PDF should be generated and attached to the record referenced in Step 1.
In addition to generating PDFs from an HTML template, we also support generating a PDF based on a "fillable" PDF template. This template is implemented as a managed document using either a fillable PDF with mapped fields, or a standard PDF with an inline signature.
Fillable PDFs are a type of PDF where the fields are mapped to a data structure contained in the PDF itself. A fillable PDF can be uploaded as a managed document, parsed, and fields in the PDF structure can be mapped to record values in an HR table. The basic process for this:
When generating a PDF using a fillable PDF template, all PDF variables will be automatically mapped to values in the table.
In addition to allowing fillable fields, a "Signature area" can be defined while creating or modifying a PDF template as follows:
The following script includes are important for fillable PDF functionality and are included here for reference:
Please see Add or Modify an HR PDF Document Template in our official documentation for more information.
The TinyMCE editor allows rich text creation that the PDF generator was not designed to support. TinyMCE is the standard editor for the ServiceNow platform, so it can’t be simply updated to not allow certain HTML elements that PDF generation supports. Some of the more common questions we’ve seen involve expectations of the following non-supported HTML elements:
This is very helpful! Thank you Michael. A question for you on the workaround for a page break (in the context of HR document generation)...
Where would we invoke document.addNewPage(). Is it added inline with the HTML on the Document Template?
The use case is for Offer Letters that legally require new pages for Exhibits, Addendums etc.
Hello, sorry for the late response.
It needs to be added into the code where the document generation is happening. If you look in our GeneralFormAPI script include, the addNewPage is on line 8.
body = this._parseBodyForImagesFromLib(body);
if(!gs.nil(body))
this.document.addHTML(body);
if(!gs.nil(pages)){
for(var i in pages){
if(i != 0)
this.document.addNewPage();
if(!gs.nil(pages[i].heading))
this.document.addHTML(pages[i].heading);
if(!gs.nil(pages[i].svg) && !gs.nil(pages[i].svg.content))
this.document.addSVG(pages[i].svg.content, pages[i].svg.position);
if(!gs.nil(pages[i].table))
this.document.addCells(pages[i].table.cells, pages[i].table.row_length);
}
}
// At this point iteration through GeneralFormAPITable(s) has finished
// and all HTML has been parsed now we just close the document and
// stop the writer
this.document.stopHTMLParser();
// Now the finished PDF file is ready to be used
var pdfFile = this.document.get();
Thanks Michael - I am using the GeneralFormAPI but still unclear on how to use the addNewPage() function. If I try the below, I would expect to see 3 new pages before the actual HTML of the document however it does not create any additional pages.
createPDF : function(body, pages) {
body = this._parseBodyForAttachmentSysId(body);
body = this._parseBodyForImagesFromLib(body);
if(!gs.nil(body))
this.document.addNewPage(); //ADD NEW PAGE TEST
this.document.addNewPage(); //ADD NEW PAGE TEST
this.document.addNewPage(); //ADD NEW PAGE TEST
this.document.addHTML(body);
if(!gs.nil(pages)){
for(var i in pages){
if(i != 0)
this.document.addNewPage();
if(!gs.nil(pages[i].heading))
this.document.addHTML(pages[i].heading);
if(!gs.nil(pages[i].svg) && !gs.nil(pages[i].svg.content))
this.document.addSVG(pages[i].svg.content, pages[i].svg.position);
if(!gs.nil(pages[i].table))
this.document.addCells(pages[i].table.cells, pages[i].table.row_length);
}
}
I actually found the solution to this and it was in the GeneralPDF API that I had to make the change. Thanks again for the help!
Hi Thanks for the article. We have a similar requirement to create a PDF Document from HTML Document Template. Do we need to add the code in UI action or how does it work. We are not clear. Please help.
Thank you! This post has been incredibly helpful in building on and utilizing the PDF Generator.
The functionality that is most needed (to be made public) is the PdfReader and PdfStamper classes, so that we can do merges onto PDF Documents and between PDF Documents. It is obvious that all of the functions in the GeneralFormJava.PdfParser object use these classes, so the functionality is present, but it is not clear (or not possible) how to access and use them.
Is it possible to define size of images in body? Currently we are able to render images only in 1:1 ratio not bigger/wider than page, smaller images are rendered small, bigger are omitted.
How to specify header/footer images in
GeneralFormAPI.SetDocument(header, footer, headerLocation, footerLocation);
? Are there any limitations? We are not able to render header neither way.
var fileName = 'Test PDF filename 66';
var targetTable = 'u_hr_ssc_case';
var hrCaseNumber = 'HRS0533090'; // = 'b1513ecadb636340b13dd06b68961998';
var targetGR = new GlideRecord( targetTable );
targetGR.addQuery('number', hrCaseNumber);
targetGR.query();
targetGR.next();
var targetTableSysId = targetGR.getUniqueValue();
var templateGR = targetGR.u_pdf_template.getRefRecord();
var body = templateGR.u_body;
var headerImage = _getTemplateImage(templateGR.u_header_image);
gs.log(templateGR.u_header_image.getDisplayValue() + " : " + headerImage);
// instead of GeneralFormAPI the DHLHR_pdfGenerator wrapper for general handling of ${variable} could be used
var pdfGen = new GeneralFormAPI( fileName, targetTable , targetTableSysId );
pdfGen.setDocument( headerImage, '', '', 'Left', '', 'A4' );
pdfGen.createPDF( body, 1 );
function _getTemplateImage(attachmentSysId){
// gs.addInfoMessage('Going to add image of ID= ' + attachmentSysId);
if(gs.nil(attachmentSysId))
return '';
var imageAttachment = new GlideRecord('sys_attachment');
imageAttachment.get(attachmentSysId);
var binaryData = new GlideSysAttachment().getBytes(imageAttachment);
var base64Image = GlideStringUtil.base64Encode(binaryData);
if (!gs.nil(base64Image))
return 'data:image/png;base64,' + base64Image + '';
else
gs.log('Issue found with getting the image');
return '';
}
edit: went through some scripts and understood that since Jakarta footer/header image strings are not in expected in the URI form but base64 encoded source of it.
tried to implement it, but no success yet, neither when sorrounded by <img src= .... > nor as "data:image/png;base64, ... " only.
I need to change the assignment group when pdf is signed by user. Could you please let me know how to figure it out.
Hi,
I am trying to add new page but i could not get it. how did you do it by using GeneralPDF Please .
Regards
JG
It is generating sing page. It is not generating additional pages if i use addNewpage() function in General PDF
Version 5.5.2 of the iText PDf generator is now considered deprecated. The FAQ link in this article goes to a page not found. Will the itextPDF generator be updated in a new release?
Correct. We also need for our customer that the "PDF Functionality that is not Supported" gets supported.
Is there any updated implementation available?
I have found that these features have restricted support. The HTML that is required is basically HTML 4 and can't be done with CSS.
So have received the response from ServiceNow that an upgrade of iText IS in fact planned however it has just been bumped to Paris release 😞 so minimum 1,5 years wait for that.
So without the above listed missing features the oob pdf generator is more or less useless 😞
A simple thing as font-size cannot be done. I have tried modifying SI GeneralPDF but nothing I have done affects the font-size. I have been able to change the margins but not fontsize.
Have anybody found another alternative to generate PDF from completely formatted html? I have tried jsPDF but none of the methods I have tried result in a pdf that is usefull. Either the table margins are gone, cannot be changed or totally off. Other methods results in blurry pdfs because it is based on screenshot and this also does not include multiple pages.
@Tommy Jensen,
After my previous comment questioning this I managed to get my current pdf generator to work for the most part. It does have limitations, but I have been able to affect font size. You do need to create HTML, but it doesn't have to be a table. For example:
parsedBody += "<span><font size='2'><b>Customer Name: SIGNNAME</b></font></span><br>";
What I have found is that the current PDF generator appears to parse HTML 4 better than current HTML standards. So it's looking for things that are deprecated by current HTML standards. Hopefully this helps.
Great, thank you that tag worked. I am building the entire document in html via script so don't mind this.
Do you happen to know why size of an image is not respected? I have included width & height on the <img> tag but the image is displayed full size (2000x2000) instead of my size 100x100. I would hate to resize the image manually to 100x100.
Images are definitely one of the weaknesses of the current utility generator. I believe you'll either have to resize the image or set the width of the image through a containing table.
On a related note a basic image tag where the source of the image is a URL is not going to work reliably. If you look at the built in function that gets a signature from the Work Order completion signature process it looks like this:
_get_signature : function(){
var gr = new GlideRecord('signature_image');
gr.addQuery('user', gs.getUserID());
gr.addQuery('table', this.tableName);
gr.addQuery('document', this.tableId);
gr.orderByDesc('sys_created_on');
gr.addActiveQuery();
gr.query();
if (gr.next()){
if (!gr.is_drawing) {
var signedName = gr.signed_name;
signedName = signedName.replace('size="7"', 'size="3"');
return signedName;
} else{
var imageAttachment = this._get_attachment(gr.sys_id);
var attGr = new GlideSysAttachment();
var base64Image = new GlideStringUtil.base64Encode(attGr.getBytes(imageAttachment));
if (!gs.nil(imageAttachment))
return '<img src=\"data:image/png;base64,'+ base64Image + '"/>';
}
}
return '';
},
Notice the return. It's creating an image tag; however, the source is essentially the base 64 encoded content of the image signature. If you aren't already doing this you will need to do so for your image. This is a much more reliable method of putting the image into the PDF document.
The image is already converted to base64 format. So I will try to imbed it into a table.
Anyone could made it work on Madrid/New York?
Exactly what ? The pdf generation with its limitations does work. But the itextpdf engine will not be upgraded until earliest with Paris release 😞
I've been struggling with this for a long time as well, I have created a couple of 'workable' pdf documents, but as soon as you use tables with borders it turns ugly.
My previous version I counted lines as to create manual page-breaks, but when text suddenly spans over 2 rows you're already screwed, fortunately I got away with it on those PDFs.
Now I created a new createPDF function in the GeneralFormAPI which starts with this:
//Create PDF for
//@input pages contains array of body-templates
createPDF_PM : function(pages) {
for (var i=0;i<pages.length;i++) {
pages[i] = this._parseBodyForAttachmentSysId(pages[i]);
pages[i] = this._parseBodyForImagesFromLib(pages[i]);
if (i>0)
this.document.addNewPage();
this.document.addHTML(pages[i]);
}
Instead of expecting 1 body like the original function, I provide it with an array of bodies, which I built up from different PDF-templates.
The function _getDocTempleInfo in GeneralDocFormV2 which builds the body now fills that array:
_getDocTempleInfo: function(tableName, templateId, instance, templateCount) {
var gr = new GlideRecord(tableName);
if (gr.get(templateId)) {
var headerId = gr.u_header.getDisplayValue() ? gr.u_header.getDisplayValue().substring(0, gr.u_header.getDisplayValue().indexOf('.iix')) : '';
var footerId = gr.u_footer.getDisplayValue() ? gr.u_footer.getDisplayValue().substring(0, gr.u_footer.getDisplayValue().indexOf('.iix')) : '';
//this.headerImage = gr.u_header.getDisplayValue() ? instance + gr.u_header.getDisplayValue() : ''; //depreciated in Jakarta
//this.headerImage = instance + "Print-logo-workorder.jpgx"; //test case in Jakarta
this.headerImage[templateCount] = this._getTemplateImage(headerId);
this.footerImage[templateCount] = this._getTemplateImage(footerId);
//this.footerImage[templateCount] = '';//gr.u_footer.getDisplayValue() ? instance + gr.u_footer.getDisplayValue() : '';
this.footnote[templateCount] = gr.u_footnote + '';
this.headerPosition[templateCount] = gr.u_header_position + '';
this.footerPosition[templateCount] = gr.u_footer_position + '';
this.pageSize[templateCount] = gr.u_page_size + '';
this.body[templateCount] = this.parseBody(gr.u_body + '', instance);
}
},
I filled the other stuff like arrays as well, though I don't use the headerImage, footperPosition etc, since all is built up in HTML.
This finally creates a definite page-break!
Have anybody found out how to change line spacing ? The "line-height" is not respected by the generator no matter where I place it in the html 😞
This is the only thing that works:
<table style="font-size: 12pt; font-family: Arial; border-color: #000000;" border="1" width="100%" cellspacing="0" cellpadding="4">
But it's still far from pretty...
I use it to make sure the text doesn't hit the border of the table, but like I said, far from pretty...
Has anyone successfully used the footerPosition?
If so can you give an example of a footer added using this?
Has anyone been able to set the meta data on a PDF they generate? Such as Title, Language, etc? Or the Tags? These things are essential to creating an accessible PDF - https://itextpdf.com/en/resources/books/best-itext-5-questions-stack-overflow/how-can-i-generate-pdf...
The GeneralPDF Script Include makes reference to addTitle(), but the value is hard coded blank. If I change this it doesn't make a difference anyway.
From the API reference, it looks as though it should be possible to set the meta data appropriately but I can't get it to work in ServiceNow at all. Anyone else had any luck?
I have not tried that so don't know for sure if it works or not. But I would really be surprised if it DOES work..... All the scripts for pdf really look very unfinsihed. There are so much code that has been commented out and that is not the proper way to release scripts in my opinion. When I see all that commented out code I think "hmm they attempted to implement styling but they could not get it to work so just commented it out", leaving it so we as admins really do not know if the code does not work or it has been left there as an example of what could work. I really hope they improve this drastically in Paris. I have a customer that had to invest in external pdf conversion (based on headless chrome) because the oob pdf generator is so bad.
Agree with you regarding the state of the code, Tommy. It's pretty frustrating. I'll log an Idea. They need to up their game to meet WCAG 2.1 for public sector organisations in the UK.
You can vote on the existing idea here: https://community.servicenow.com/community?id=view_idea&sysparm_idea_id=eb01d6d3db608cd85ed4a851ca96...
Yeah, already spotted that and voted. I raised another to specifically address the accessibility issues we're facing.
I am trying to change font family on the HTML HR document. If i have to use OOTB SI General PDF to generate pdf then where exactly should i add <font face='times'>, can you guide me.
You can try to do it with a style="" statement on the div/table etc you what to change font for. But it is not likely to work. the iTextpdf engine do not support a lot of styling.
We are generating PDF for a HR Case by defining HTML document template then using PDF Generator attaching PDF to the case.
Customer wants to have font family as times on PDF.
May I ask where are you inserting this code to modify the font size?
Well either in General Document Templates using Source code:
Or in a PDF Script as used as above.
The ServiceNow Paris release has finally replaced the iText5 with the iText7 PDF generator.
I've tried some styling to my tables, but still nothing seems to work...
(Fortunately current styling still works though)
Do I also need to update global.GeneralPDF script include to handle different stylings?
And if so, does someone have an example on how to use the new options provided with iText7?
Where do you see that this upgrade has been done?
The open software pdf on stats.do has not been updated. There it still says itext 5 ( I am assuming that file is autogenerated )
And I don't really see any update to iTextPDFUtil so that would definately need to be updated also.
And the "Idea" is still just in "In consideration".
Hi AliJubran,
I am arriving two years late to this party! Could you show how you have used addNewPage() in the GeneralPDF script to achieve this?
Thank you
Ow...
I saw this document:
And it says under HR Service Delivery:
HR properties
Support HTML document conversion to PDF using iText7. iText7 has an extensive library that handles a wide variety of formatting, signature merge, and pre-fill values of HR documents.
So I thought finally!
Aaaaah I didn't update that property though...
Only had 1 day to test this, so now i have to wait until we actually upgrade to Paris...
Well do let me know if it works. However I actually doubt it because I see no change in the script include. I have a feeling that the doctumentation about the properties has been included too soon and so have no real effect.
I posted a solution for multi-page PDF a few comments down 😉
Thanks Chris. I see it now. Neat solution! I will try it out.
You're welcome!
I did get this working for me. Thanks again 🙂
Good to hear!
It's always nice when made solutions help others as well 😉
Can the PDF Generator be used to merge multiple PDFs into a single PDF? The use-case is an employee attaches several documents pertinent to a particular HR Case. The HR Case workers would rather work the single document.
Hi Every one,
I need help on PDF generation.
I am able to get the PDF generated with two fillable fields but the new pdf which is getting generated gets completely different font for the dynamic contents.
My issue is how to change the font of the dynamic fields even though we change the font of the dynamic field on the PDF Template form it doesn't work.
I am generating a pdf template using the below code.
var fieldMap = new Object();
fieldMap["full_name"] = current.u_full_name;
fieldMap["m_ref"] =current.number;
var newPDFDocument = new sn_pdfgeneratorutils.PDFGenerationAPI;
var result = newPDFDocument.fillDocumentFields(fieldMap, "e82ba25ddbc2b8508d97449e3b96192f", current.sys_class_name, current.sys_id, 'PDFGEN_'+ current.number);
Parameters for function fillDocumentFields
1st parameter is list of fields on the pdf which needs dynamic Generation
2nd parameter is the sys_id of the attachment record (pdf) associated to the Document Template
3rd parameter is the new table(incident)
4th parameter is for new sys_id of the (incident record) on which we want to attach the newly generated pdf
5th parameter is for the file name of the PDF
I can see fillDocumentFields function even accepts the sys_id of the font which is the 6th argument but anyone knows how to use.
Thanks
James
Please refer to the following post:
This shows you how to use the new PDF generator (which works ALOT easier)
Hi,
We have the same question.
Did you find a solution ?
Thanks