I am a creator — PDF Document Generator - Walter Brame

walter_brame
ServiceNow Employee
ServiceNow Employee

------------------

EDIT

------------------

A lot has changed over the years.

Be sure to check here first:

https://developer.servicenow.com/dev.do#!/reference/api/quebec/server/sn_pdfgeneratorutils-namespace/CellBothAPI?navFilter=pdf

Have also enjoyed repurposing Webkit HTML to PDF plugin:

https://docs.servicenow.com/bundle/geneva-performance-analytics-and-reporting/page/use/performance_analytics/task/t_ActivateWHTP.html

------------------

ORIGINAL

------------------

PDF Document Generator is used by the ServiceNow, Inc. sales organization and is a generic HTML form creation and PDF Document generation framework.   The framework supports creating HTML forms and PDF files using any UI Form in the instance as a template.

 

The PDF Document Generator will replicate how that standard UI Form looks but then provides the functionality to change it easily as a front-end user without requiring development.

 

A PDF feature exists today in the standard platform but it is not customizable. The PDF Document Generator allows for rapid deployment of multiple templates and making changes on the fly to every component visible on the screen very quickly and easily.

 

Previously, the Sales organization was challenged with the ability to produce Quotes and Sales Orders from a CRM environment. With this custom ServiceNow, Inc. app in place, it is now possible for the front-end business users (sales operations, systems managers, administrators, power users, non-developers) to access configuration tools that put control of the following features directly into their hands:

 

- Add/remove fields as needed

- Change any fields placement and the field's label, orientation, or value

- Change any section/grouping or fields / Table of information (E.g. "Section 1", "Section 2", etc.)

- Add/remove new sections present in the form

- Manage an entire section's placement, splits, annotations, and headers

- Re arrange the placement of components in the form (E.g. one component relative to the next)

- Change the look and feel (color, border, padding, alignment, etc.) via common HTML and CSS style attributes.

- A scripting API to create dynamic conditions to change any aspect of the form and data (style, value, label, section, etc.).

- Save templates of form layouts

- Preview and print to PDF

 

The functionality of this PDF Document Generator was made possible by leveraging the ability to write custom scripts in the standard ServiceNow platform, and by having access to the backend table structure and data where forms are stored in the instance (standard UI page, CSS, HTML, and JavaScript).   A single developer, in a time frame of one business quarter, built the PDF Document Generator.

 

pdf_doc_gen_img_1.png

pdf_doc_gen_img_2.png

pdf_doc_gen_img_3.png

pdf_doc_gen_img_4.png

pdf_doc_gen_img_5.png

pdf_doc_gen_img_6.png !

 
99 REPLIES 99

walter_brame
ServiceNow Employee
ServiceNow Employee

Hi Romain,



Q. "everytime when I click on "preview" i've a blank page with this message. How can I bypass the error message for edit my box in preview mode ?"



A. To bypass that error use a different "Theme" or make the edits directly in the "Elements" Table where all of the formatting is saved. I will explain...



The issue you show is problem with the current application because I have not added validation to the fields that are used to write the formatting. Currently with out documentation on how the syntax should be written and with out validation it is not very helpful. Ill work to add the validation.



From what you describe it does sound like the UI Page is breaking when some formatting syntax is different than what the application is expecting. If you are writing formatting and the UI Page stops working here are a couple things you can do to try and identify what the issue may be.



( 1 ) Load the UI Page with out a "Theme".



* A little bit of background:



The "Theme" is just a term used to describe the "Element" records to use for the formatting. By default the application installs a "Theme" called "base".



The URL to the default Theme would be the following when you add your instance name:



https://<your_instance>.service-now.com/general_element_list.do?sysparm_query=u_view_id%3Dbase



Notice the query is in the Elements ("general_element") table. This is the table where all of the formatting is stored.



* Now to make the changes:



Click on the "Preview PDF" button and copy the URL from the Web Browser address bar. The URL should look something like the following:



https://<your_instance>.service-now.com/GeneralForm.do?sysparm_tableid=705dfed54158210006ff035e3ab2cc51&sysparm_table=u_pdf_document_generator_demo&sysparm_themeid=base



Notice the filter criteria in the URL for "sysparm_themeid=base", this is how you tell the UI Page to use a "Theme". Remove the entire filter for the Theme and load the new URL into a new Web Browser window. This will tell the application not to use a Theme. The new URL should look something like the following:



https://<your_instance>.service-now.com/GeneralForm.do?sysparm_tableid=705dfed54158210006ff035e3ab2cc51&sysparm_table=u_pdf_document_generator_demo



The UI Page should have loaded with out a "Theme" which means all we see is black text on a white background and everything should look smashed together or not aligned very nicely.



*** If you are writing formatting and the UI Page stops loading then try loading no Theme -Or- create your own different Themes for testing purposes and swap which one to load. This should give you a troubleshooting mechanism to determine if the UI Page is not working as a result of writing formatting.



( 2 ) Locate the formatting with unexpected syntax and fix or remove it



All of the formatting data for the form is stored in the "Elements" table (i.e. general_element by default) which you can get to by filtering the left navigation for the "PDF Document Generator" Application and then clicking on the "Elements" Module. For Example:




https://<your_instance>.service-now.com/general_element_list.do?sysparm_query=




Either sort the table by "Updated Last" and possibly you can identify a formatting issue by reviewing the last record updated.




-Or-



Search the table for the error message for example if your error message is complaining about "align" you could search the Table in the Attributes, HTML, Styles, and Script fields to try and find the record that may have an issue.



This also gives you the ability to troubleshoot any issues related to formatting.



=========



A few Notes on formatting syntax:



* "align" being an HTML Attribute needs to be entered in the "Attributes" field (Not the "Styles", "Properties", nor "Scripting" fields). The syntax needs to be:



html_attribute_name="html_attribute_value"; align="justify";



Notice how it uses double quotes and not single quotes and each Attribute must be separated by a semi colon.



* If you wanted to script the alignment to make it change dynamically based on some conditions then that would be entered into the "Scripting" field. The syntax needs to be:



this.element.attributes = ' align="justify" ';



Thanks,


Walter


Your anwser is perfect Walter. My bug is fix, and I understand much more how it works !


Thank you, really.



Last question, how I can easily setup a thing like that :



[static sentence][dynamic value][static sentence]



for exemple dynamic value is : 02/08/2014


So, "Your registration date is 02/08/2014 and you can blabla..."


Or how can move the dynamic value for simulate a "sentence" because I can't specified a CSS like height or width for position and if I use cell-label-orientation it's too much approximative.



I recovered my dynamic value easily, my static values are integrated, just merge both and it's perfect.


Hope you have some time for reply and again thank's a lot !


walter_brame
ServiceNow Employee
ServiceNow Employee

Hi Romain,



Glad to hear it!



Q. How I can easily setup a thing like that :



[static sentence][dynamic value][static sentence]



for exemple dynamic value is : 02/08/2014


So, "Your registration date is 02/08/2014 and you can blabla..."



A. This is easily acheived by using the Scripting API to read the form to find variables and replace them with text. Explained below...



[] Some background:



1. Every single value that is displayed to the user in the UI Page is contained in a "Cell" and is either a "Label" or a "Value".



For example:



* An Annotation, UI Formatter, and HTML WYSIWYG fields text will be written as a Cell - > Value



* Normal Fields present in the " Main Form (Not an embedded list) "


will be written as a Cell and the Label and Value of the Cell will match what is in the Platform Form Layout.



* Embedded Lists are written as a Cell -> Label for the List Headers and a Cell -> Value for the values in the List Columns.



2. When you add a script it is interacting directly with these Cells.



For example:



If you want to read the value of the Cell you would write something similar to:



var cellValue = this.element.value; // This is the Cell - > Value



If you want to read the Label of the Cell:



var cellLabel = this.element.label; // This is the Cell - > Label



[] Now the changes



1. First I add an Annotation to the Form Layout and store the static text:



"Your registration date is {!dynamic_registration_date} and you can blabla..."



Entered exactly as written above just with out the double quotes on the ends.



The string "{!dynamic_registration_date}" is our "Dynamic Variable Name" is what the


script will replace with some actual text.



2. Next you need to target the appropriate elements to run the script against



To apply formatting to the Elements they may be targeted by their HTML ID, HTML Class Name,


or HTML Tag Name. When creating a formatting record the following fields in the image below


are used to target an element in the form. As you can see in the notes in the UI under


Element Target Information each record should only use one of the choices to target an element.



pdf_doc_gen_look_&amp;amp;_feel_targetting_elements.png



Tip: If there are multiple formatting records targetting the same element in the form it


will produce unexpected results because the application will not know which one to use.


If any formatting is not being applied you should check if the element has more than one


formatting record targeting it.



When manually creating a formatting record for a element in the "Elements" (general_element) table you can target any element.



For example you could target:



Any Table


Any Row


Any Header


Any Column



Just view the HTML Source of the Preview PDF UI Page to learn the HTML IDs and Class Names


being generated for your Form so you can target those.



For example if you created a formatting record to use the "Element Tag" field and target " td ".


Then that record will run its formatting, scripting, etc. on every single Td tag in the form.



3. Then you write the script to find the matching static string and replace its text



Lets say I target one specific Annotation element directly by its ID then in that case


I know its the right place and the script just needs to run.



The Script:




if(this.element.value.indexOf('{!dynamic_registration_date}') != -1) {


        this.element.value = this.element.value.replace('{!dynamic_registration_date}',


        '02/08/2014');


}




Maybe I run this script on every single Cell in the entire Form to look for multiple


dynamic variables then in that case I need to use some logic to change how the script runs.




The Script:




// Run on all Annotations


if (this.element.type == 'annotation') {


      if (this.element.value.indexOf('{!dynamic_registration_date}') != -1) {      


              this.element.value = this.element.value.replace('{!dynamic_registration_date}',


              '02/08/2014');


      }


      if (this.element.value.indexOf('{!dynamic_customer_name}') != -1) {      


              this.element.value = this.element.value.replace('{!dynamic_customer_name}',


              'Romain');


      }


}




You can call any other Script Includes from here to help store logic which determines the


what text is added in a different place:



if(this.element.value.indexOf('{!dynamic_registration_date}') != -1) {



      // Maybe we need the name of the company or some other field to figure something out


      // so get the root GlideRecord of the entire Form


      var glideRecord = this.element.record;



      var myDynamicVariables = new MyDynamicVariables(glideRecord);


     


        this.element.value = this.element.value.replace('{!dynamic_registration_date}',


        myDynamicVariables.getRegistrationDate());


}


christophe_musi
ServiceNow Employee
ServiceNow Employee

Hi Walter,



This seems very interesting, and it could help one of my customer.



So I have installed the application and tried to play with the demo (version 1.1 on Dublin). I can actually preview and create PDF, but the content of the PDF generated differs from the preview. I have attached a screenshot of the result of Create PDF on the 3rd record in the demo. The Preview PDF shows correctly all the fields.


The header part with the information of the record is not displayed. I did not change anything in the configuration, just tried to test the demo.


Screen Shot 2014-06-11 at 5.55.54 PM.png


Am I missing something here ?



Thanks,



Christophe


Hi Christophe,



What you have found there is a bug in the application which I just fixed and uploaded as Version 1.2. Thank you very much for pointing it out!



Specifically what is happening is an image in a UI Formatter in the first section in the Form does not have an absolute URL and it causes the PDF generation to fail. The reason so much content was missing from the PDF file is because currently the system will skip an entire Form Section if it has any issues. Since the image could not be loaded it caused the entire first section of the Form to not be rendered in the PDF.



The reason the image does not have an absolute URL is because a script is used to dynamically write the URL so that it works in any instance you move the application to. The script failed so the URL is never written and it produces the problem you are pointing out. The reason the script failed is mentioned below under the version 1.1 fixes as a data record for table "general_element" for sys_id "ca52e39d4158210006ff035e3ab2cca9".



[] Version 1.2



1.2. Main File:


(Dublin) "sys_remote_update_set_0579dd786f916100e3eb1665bd3ee445.xml"



(Calgary) "sys_remote_update_set_4762f934b495a500178ecdf09e3c3c86.xml"


Calgary Download:


https://share.servicenow.com/sys_attachment.do?sys_id=553439b02b916100ffee554069da1517



1.2. Patch File:


(Calgary and Dublin) If you just want to patch version 1.1 with the fixes use file: "sys_remote_update_set_8073d9786f916100e3eb1665bd3ee4f5.xml". This stand alone update can safely be applied to both the Dublin and Calgary releases of PDF Document Generator Version 1.1. This will contain all of the fixes included in Version 1.2.


Calgary and Dublin Patch 1.2. Download:


https://share.servicenow.com/sys_attachment.do?sys_id=7fa375b02b916100ffee554069da151b



Version 1.1 Fixes:



- Script Includes GeneralFormReferences, and GeneralFormRelationships, "descending" was miss spelled. This is a configuration to set the value for the type of sort for a list in the form. Also the same logic used by both of these classes was moved behind the "configure" method in GeneralFormListInfo.



- Script Include GeneralFormListInfo was missing any functionality to utilize the "query" field from the "general_list" table. Added "configure" method to centralize the functionality to be used by many different types of list builders, e.g. GeneralFormReferences, and GeneralFormRelationships.



- Script Include GeneralFormLists was changed to allow "getListInfo" method to be used with the new "configure" method in GeneralFormReferences, and GeneralFormRelationships.



- Data Record for table "general_element" for sys_id "ca52e39d4158210006ff035e3ab2cca9" has a script that is used to dynamically build the URL for the image for the company logo. The script was referencing an invalid Class so the image becomes invalid causing the entire section containing the image to not be rendered. There were two files for the same sys_id and one of them was incorrect.



- Script Include GeneralForm2 in "getLists" method had a reference to a table "sales_opportunity_item" that is specific to the ServiceNow, Inc. implementation of the application.



Thanks,


Walter