Framework for Generating Custom NetSuite PDF Files

Inspired by the work completed in a recent article, How To: Leverage NetSuite’s Advanced PDF / HTML Generation Tools, and from a LinkedIn NetSuite user group discussion and suggestions by Daniel JR Arena, Troy Oltmanns, Corey Hunt, and David Smith, I solved a client request to generate custom PDF files based on a custom record object.

PDF Generation Architecture

The architecture summary is as follows:

  1. PDF templates will be stored in a special user designated NetSuite folder.  We can then use this for a template selection list on the custom record itself.  The client can add more templates as needed without a change to the code.
  2. The basic PDF templates will conform to NetSuite’s implementation of the Big Faceless Report Generator. ¬†Because it is not available by NetSuite for custom PDF files, it will not use the FreeMarket syntax as referenced in NetSuite’s Advanced PDF / HTML implementation.
  3. The data to be merged into the template will take advantage of JavaScript JSON and the capacities built into the TrimPath Template engine.  This saves us time by not writing our own parsing routine and it gives us control and looping logic, if needed.
  4. Users can select the template they are interested in while working with the record; By simply clicking a button, a custom SuiteLet will open in a new browser window to display the resulting PDF.


Custom SuiteLet to Generate the PDF File

Below is the custom SuiteLet to generate the PDF file.  The key to this function is to get the URL parameters to reference the custom record internal ID and the template ID.  The rest is processing logic.  It is assumed that the TrimPath Template Engine has been uploaded and is referenced as a Library function.

function id002_bfo_create_note()

	var func = 'id002_bfo_create_note ';
	var id = request.getParameter('id');
	var template = request.getParameter('template');

	nlapiLogExecution('AUDIT', func + 'with id | template:', id + '|' + template);

	try {
		// load the custom record
		var rec = nlapiLoadRecord('customrecord_dffg_note', id);

		//at this time, we have one node called "note".  But we can add more data elements as needed later
		if (rec){
			var recJSON = JSON.stringify(rec) ;
			var data = {note : eval('(' + recJSON + ')') };

			nlapiLogExecution('AUDIT', func + 'JSON Data:', JSON.stringify(data));
		} else {

		//load the template data (JST plus BFO Syntax)
		var templateJST = nlapiLoadFile(template);
		var xml = templateJST.getValue();

		// now we want to call TrimPath to parse the XML in JST format
		// remember that the String object has been prototyped to call TrimPath.parseTemplate on our behalf
		var result = xml.process(data);

		nlapiLogExecution('DEBUG', func + 'XML:', nlapiEscapeXML(xml));
		nlapiLogExecution('DEBUG', func + 'RESULT:', nlapiEscapeXML(result));
		var file = nlapiXMLToPDF( result );
		response.setContentType('PDF',id + '.pdf', 'inline');
		response.write( file.getValue() );   

		var errorStr = (ex.getCode != null) ? ex.getCode() + ' ' + ex.getDetails() + ' ' + ex.getStackTrace().join(' ') : ex.toString();
		nlapiLogExecution('DEBUG', func, 'A problem occurred: ' + ' ' + errorStr);

Sample PDF Template Illustrates JSON Data

To help understand what is happening, I have screen-shotted the JSON data that is returned from NetSuite’s standard nlapiLoadRecord call. ¬†How convenient because we simply supply the entire row to the template engine without any extra work. ¬†Resist the urge to touch the data — the TrimPath JST framework¬†has utilities to format your data.

With the JSON data, you then can have very simple calls to your PDF XML template file following Big Faceless Organization (BFO) guidelines. 

Finally, the SuiteLet outputs the result which gave our client full capacity to produce the template and PDF without needing a SuiteScript developer.  They like that.

Get Help Optimizing NetSuite

NetSuite was designed to be optimized to fit requirements well. If you would like help to get the most out of your NetSuite system, contact us.

Be Sociable, Share!

Marty Zigman

Holding all three official certifications, Marty is Southern California's NetSuite expert and leads a team of senior professionals at Prolecto Resources, Inc. He is a former Deloitte & Touche CPA and has held CTO roles. For over 25 years, Marty has produced leadership in ERP, CRM and eCommerce business systems. Contact Marty to set up a conversation.

More Posts - Website - Twitter - Facebook - LinkedIn - Google Plus - YouTube

| Tags: , , , , , , | Category: NetSuite, Reporting, Technical | 12 Comments


  1. Corey Hunt
    Posted January 4, 2014 at 5:01 pm | Permalink

    Very nice. That’s much cleaner than my solution. I wish I had know about TrimPath when I made it.

  2. Posted January 5, 2014 at 6:53 am | Permalink

    Thanks to the contributions you and the others provided in the LinkedIn group discussion, this solution was inspired. I see a class of problems I want to solve under this approach. One is a custom email generator alternative. Sure feels good to take control over our destiny.

  3. Posted January 5, 2014 at 11:44 am | Permalink

    Thanks for sharing!

  4. Amol
    Posted November 19, 2014 at 1:48 am | Permalink

    The above code is nice one but I have one problem I have to create .txt file.
    1.How it is create?
    2.That file have hexadecimal coded language which is generated on button click.which convert whole existing form in hexadecimal code. How it create? please tell me answer.

  5. Amol
    Posted November 19, 2014 at 2:49 am | Permalink

    How to create custom bar,pie,etc. chart though portlet? please give me example…
    Thank you.

  6. Posted December 9, 2014 at 9:58 pm | Permalink

    Hello Amol,

    The output is really all about strings. So you can change the code element that currently illustrates response.setContentType(‘PDF’,id + ‘.pdf’, ‘inline’) to response.setContentType(‘PLAINTEXT’,id + ‘.txt’, ‘inline’).

    As am not sure what you mean by Hexadecimal coded language. Can you provide an example?


  7. Posted December 9, 2014 at 10:03 pm | Permalink

    The Portlet object works with these types:

    1. Lists
    2. Form
    3. HTML
    4. Links

    The only one that can draw those graphical elements is HTML. Yet, the HTML can be anything. You won’t easily be able to draw NetSuite provided graphical elements as these are built with NetSuite provided portlet objects in the drag and drop interface.. Instead, you might want to consider using something like the Google Charts API. What you can do is get the data you need, call the Google APIs and draw it in the HTML portlet. It’s basically server side and client side JavaScript to pull it all together.

  8. Matt Gaspar
    Posted January 9, 2015 at 9:45 pm | Permalink

    Great article and blog! I implemented a solution based on your article using Handlebars.js as the template engine. I noticed that TrimPath hasn’t been updated since 2008 and I try to avoid using any abandoned open source projects! Also Handlebars has a lot more functionality and has an ability to precompile the template for much faster execution.
    There are actually a lot of options for javascript based template engines.

    I also opened an enhancement request with NetSuite to allow passing a custom javascript object into Freemarker (Advanced PDF). It should be possible but NetSuite’s api current only allows native Netsuite object types. I don’t see any reason not to make it more useable.

  9. Posted January 9, 2015 at 10:30 pm | Permalink

    Hey Matt,

    Most interesting. Handlebars looks most useful and I agree, that old TrimPath template engine has not seen any updates in too long. I have been more bullish on FreeMarker and the Advanced PDF because it appears that we can indeed drop in record objects to the template model when we script. We have built a new communications framework that we are plugging into a customer solution but it is not yet ready for market. But I have yet to try this for a generic Javascript JSON object. I want to stay closer to the FreeMarker environment because all NetSuite users are getting acquainted with it. This means our innovations look “best practice”.

    Also, I just reviewed some 2015.1 updates and the PDF template API is becoming more robust.


  10. Kishor
    Posted August 25, 2015 at 10:49 pm | Permalink

    Hi Marty,

    Can we put water mark on Advanced PDF layout

  11. Posted August 29, 2015 at 10:00 pm | Permalink

    Hi Kishor,

    Here is an example NetSuite Advanced PDF with the watermark.

    To produce the watermark in your PDF, do the following:

    1. Create a Macro to reference a special tag
    2. Reference it in the Body definition

    See below the code snippet (thanks to Mike, a consultant on our staff, to help with this solution):

    <macro id="watermark">
    <p id="watermarkbody" rotate="-30" valign="middle" align="center">ORIGINAL</p>

    <body background-macro="watermark" header="nlheader" header-height="10%" footer="nlfooter" footer-height="20pt" padding="0.5in 0.5in 0.5in 0.5in" size="Letter">

One Trackback

  1. […] Framework for Generating Custom NetSuite PDF Files […]

Post a Comment

Your email is never published nor shared. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>