How To: Leverage NetSuite’s Advanced PDF / HTML Generation Tools

This article is relevant if you are considering using NetSuite’s Advanced PDF/HTML Template technology to produce great looking documents.


We are committed to this space.  Since we published this article in 2013, we have developed a number of other articles and tools that are most relevant for NetSuite users working to get more out of the Advanced PDF/HTML tools:

  1. Interrogating NetSuite’s Advanced PDF / HTML Templates
  2. Supercharge NetSuite Advanced PDF/HTML Templates
  3. Solved: Custom NetSuite Item Fulfillment Ship Notifications
  4. Framework for Generating Custom NetSuite PDF Files


NetSuite’s standard document generator is basic. For the most part, the supplied documents, such as invoices or statements, gets the job done. ¬†However, if you wanted more complex output formatting, you previously had to move to SuiteScript.

NetSuite uses a technology called “Big Faceless Organization” or “BFO” to enable PDF file generation driven by FreeMarker¬†template engine. ¬†A popular add-on in the Java community, these libraries are all built-in for us to use inside NetSuite behind the scenes. ¬† Elements have been available to SuiteScript developers for a number of years through the SuiteScript nlapiXMLToPDF function call.

This also means that it has been beyond the reach of many NetSuite users. ¬†However, more recently, the basic framework without the need for SuiteScript has been exposed via the “Advanced PDF/HTML Templates” feature. Once you enable this feature, you can user this tool to design your own PDF files without SuiteScript programming.

General Orientation

The template technology uses a framework that combines both XML, HTML, CSS, and BFO tags to drive the PDF generation. I have a sample invoice and the source code to help you make sense of what is happening.   Anyone with a good understanding of HTML and Cascading StyleSheets (CSS) should be able to get comfortable with this tool relatively quickly.

Header, Footer and Body Concepts

The first thing to realize is that NetSuite supplies sample templates with HTML snippets called a header, a footer and the body.   These concepts are easy to understand when we are in a document orientation.  These elements, which could have been named anything, are defined through a macro definition supplied by the BFO framework. The header and the footer then produce a page concept that repeats as the body content changes. The Body is meant to be where the main elements of your document (or report) is generated.

NetSuite Data References

The NetSuite documentation is still being developed. But here are some important concepts to understand:

  1. record: this is the primary data set you are referencing. If you are working with an invoice, you are effectively working with the equivalent of a transaction search and the results set of one invoice record object. Use the NetSuite Record Browser document located in Help for your field references.
  2. record.item: the record object has an item sublist, as well as others. The BFO framework has a way to setup loop in the document and then output the result. This tag “< #list record.item as item>” is a directive to trigger the framework to create a loop with the content located between the closing tag. It then supplies the “item” element information so you can reference the record’s sublist contents.
  3. companyInformation: this elements allows you to access the Company Information located under Setup, Company Information. For example, companyInformation.addressText will supply you your address defined globally. To get the field names, click the field label after you have activated Show Field IDs.

Subsidiary Information and One World

Unfortunately, the Subsidiary information is not accessible in the framework. Essentially, the Company Information is the root subsidiary. ¬†Most of the time, your child subsidiaries are your operating entities. ¬†You will ¬†likely want to reference the Subsidary information as it contains key information about your subsidiary such as address, Tax ID, telephone and contact information. ¬†Since you can’t reference the subsidiary records in the PDF framework, here is what you need to do:

  1. For every subsidiary field you care about, create an equivalent custom transaction body field.
  2. Source the default values for the custom field from the Subsidiary record.
  3. When the transaction record is created, the information will be copied from the Subsidiary and stored with the transaction.
  4. Reference this custom information now on the “record” object.

Ideally, all of the NetSuite elements would be exposed to the framework. However, this technique to source information from another table to your target table is a common practice when trying to expand features in NetSuite.

Trial and Error Development

I copy and paste the source code information between my favorite editor.  The process is iterative so be prepared to go back and forth many times to get the results you are looking for.

I also recommend you make local backups of your source document to disk. ¬†The document format is XML — as such, the tag layout must be perfect for the document to be valid. If you mess it up as you develop, a common occurrence, it is helpful to have a copy of an old version that worked.


Creating your own document formats can trigger your customers to know they are working with a high caliber professional organization.  The new NetSuite PDF / HTML framework puts powerful document formatting within reach of most users comfortable with HTML.

If you would like help enhancing your NetSuite account, 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 | 50 Comments


  1. cash
    Posted June 27, 2014 at 10:32 am | Permalink

    Hi Marty–great article. Question: I’m looking to create a report from Netsuite using data from a saved search. Is that possible?

  2. cash
    Posted June 27, 2014 at 12:14 pm | Permalink

    For clarification, I want a PDF generated out of netsuite, complete with company logo etc, but not just a standard form like what usually comes out of NS. Instead it would be a standard tabular report, based on a saved search.

  3. Posted June 28, 2014 at 8:33 am | Permalink

    Hello Cash,

    Indeed, this can be done. We could supply a saved search to a SuiteLet that would then link to a custom template that would format and output as a PDF. I would start by using a framework approach as we outlined in this article:


  4. Dan Speak
    Posted July 18, 2014 at 6:51 pm | Permalink

    Hi Marty, thanks for your post – I was trying to look at your ‘source code’ in the link in the body your post (just under ‘General Orientation’) but seems to be just a text version of your post? Do you have this available at all? Best Regards

  5. Posted September 3, 2014 at 8:45 am | Permalink

    Thanks Dan. I fixed up the reference!


  6. Posted September 30, 2014 at 9:00 pm | Permalink

    Thanks Marty for the reference to the syntax that NetSuite use in their advanced templating engine. Helped me to figure out some of the errors in my templates!

    Thanks again,

  7. Posted October 1, 2014 at 2:03 pm | Permalink

    I really need to build a template that will output all the variable names. That would be very valuable!


  8. Dan Speak
    Posted October 1, 2014 at 3:12 pm | Permalink

    A good cheat for finding any variable strings is to append the NS form page URL with &xml=t this displays the page as an XML page, simply search for the value that is in the field you are looking for and the string is in the XML tag next to it. Saved me hours!

  9. Posted October 1, 2014 at 4:22 pm | Permalink

    That is a valuable tip! Thank you Dan!

  10. Ken
    Posted October 23, 2014 at 9:44 am | Permalink

    Hi Marty,
    NetSuite has done another upgrade and some buttons and tabs are missing that enabled me to print thank you letters for our customers. How can find my stored letters/templates to continue sending the letters?

  11. Posted October 26, 2014 at 12:01 pm | Permalink

    With recent 2014.2 changes, it seems that the old “CRMSDK” script templates are being deprecated. But I believe they are still accessible. Are you not able to see them under Documents, Templates sub folder? Editing existing email templates will pull up the old template with an option to convert to the new Scriptable template. See image.

  12. Kathy
    Posted December 11, 2014 at 10:09 pm | Permalink

    Hi, Marty. I want to catch item detail using ${item.item.custitemid} in sales pdf, is it ok? Custitemid is the custom item filed id. And I want to get all values of this field for all sublist item and to be print in sales order.

  13. Posted December 13, 2014 at 2:24 pm | Permalink

    Hi Kathy,

    I just wrote an article about how to interrogate the Advanced PDF Data model. Unfortunately, it appears we can’t get to the built in Keys directive that would give us the data so we could see if your value is in the data model.

    In your case, I don’t think NetSuite is providing that much reach to the item table. What you are trying to reach, if I understand, is all they way over on the item record and you are limited, by the default implementation, access to the line item sublist but no deeper.

    Here is what you would need to do. Create a custom Transaction Column. Have it source its data from the item table. This will copy the value you need to the transaction line item level and then you can access it from the template tools.

    The other approach is to create your own template renderer in SuiteScript and drive in the Item table data. But that is likely more demanding.


  14. Sam
    Posted December 17, 2014 at 8:26 am | Permalink

    This article just saved my day. I am not a Netsuite developer but a request came to me from within organization to modify an exiting invoice. Going through documentation and your blog, I was able to accomplish 90% for design. But I was not able to get custom fields in the report and comment from “Dan” helped met get other 10%. And I just wanted to thank you for taking time to write this great article.

  15. Posted December 17, 2014 at 8:37 am | Permalink

    Excellent! Also, we need a way to interrogate the data model in the Advanced PDF template engine. See this article I recently wrote and Vote for a specific NetSuite enhancement:


  16. Sam
    Posted January 29, 2015 at 3:35 am | Permalink

    Hi Marty,

    I am creating a PDF using Xml files and in my xml file i am using to get the value of a list/record type field and getting the text value of that field because it returns only 1 value.
    But when i am tying to get the value of multi select type field using the same code and selects two more values for this fields i am getting value as undefined because it returns array of values.

    Please suggest how can i get all the selected values for multi select field.


  17. Posted January 31, 2015 at 11:12 pm | Permalink

    Hello Sam,

    Have you checked out the operations of Freemarker here:

    You may need to perform a Test operations but I believe sequence operators should help you get the data you need.


  18. Giriesh
    Posted February 9, 2015 at 1:38 pm | Permalink

    Hi Marty,

    I’m trying to print the expiration date corresponding to a lot number on transaction forms using advanced PDF templates. I know how I can do this using scripts, but I’d like to avoid scripting if possible. The problem is that inventorydetail contains info on Lot#, Bin#, and quantity but no expiration. Any idea how to grab the expiration date?

  19. Posted February 9, 2015 at 4:30 pm | Permalink

    I haven’t tried to walk the tree on that structure yet. Are you able to get to Lot# and Bin# (it seems like you are)? Have you tried to create a custom field that sources data from the expiration date and then reference that (cheap workaround)?

    Finally, I am convinced, at this time, we need to compile our own searches into our own data layout to get real control over this. Of course, that demands scripting…

    Let me know how it goes.


  20. Kishor
    Posted March 20, 2015 at 1:02 am | Permalink

    Hi Marty,

    I’m trying to display item description on my pdf layout but result will be come with Html item description type is Inline html
    Result is:
    Receive off per month with your Act! Business Care* subscription.

  21. Posted March 20, 2015 at 5:57 am | Permalink

    I am a little confused by what you are suggesting. Are you trying to generate a PDF or an HTML document?


  22. Kishor
    Posted March 20, 2015 at 6:59 am | Permalink

    Hi Marty,

    I’m trying to generate Advanced PDF but it show me html code

  23. Posted March 20, 2015 at 9:19 pm | Permalink

    I recommend you follow this link in the Help document. The XML for the template needs to follow the Freemarker syntax to ultimately yield PDF output. See the “Source” to get the syntax by following how NetSuite does it with their own Advanced PDF Editor:

  24. Kishor
    Posted April 9, 2015 at 12:22 am | Permalink

    Hi marty,

    how can i user keep_after() function in advance pdf layout

  25. Kishor
    Posted April 17, 2015 at 2:59 am | Permalink

    I am trying to process a mathematical expression on the contents of a map of
    string data:

    | a | 1,111 |
    | b | 2,222 |
    so a simple expression like
    ${a?number + b?number}
    i want a result 1111+2222=3333

    but sytem give me following error

    The template cannot be saved due to the following errors:
    a is not a number, it is freemarker.template.SimpleScalar
    a is not a number, it is freemarker.template.SimpleScalar
    a is not a number, it is freemarker.template.SimpleScalar
    a is not a number, it is freemarker.template.SimpleScalar

    could you help me please……

  26. Posted April 18, 2015 at 7:38 pm | Permalink

    Hello Kishor,

    I am finding that not all FreeMarker syntax is supported in the NetSuite model. I haven’t tried the keep_after function. The trick to see if it is working is to produce a variable and then apply the function. Here is the reference:

    Try this in your document to see if you get the result they are suggesting:

    ${“abcdefgh”?keep_after(“de”)} will print “fgh”

    Does this come back for you?


  27. Posted April 18, 2015 at 7:45 pm | Permalink

    Hi Kishor,

    I recommend you assign the data to variables first. I too have run into problems like this when I tried to perform operations you are working on. For example, see the follow code fragment that is looking for a character value. I found that if I first assign a variable as an integer, I can later perform math on it.

    <#assign row=0>
    <#if item.custcol_line_item_group?has_content>
    <#assign row=item.custcol_line_item_group?number>
    <#if item.itemtype != "ShipItem">
    <td class="row${row % 2}" align="center" colspan="1" line-height="150%">${item.quantity}

  28. Kishor
    Posted April 20, 2015 at 11:38 pm | Permalink

    Hi marty,

    Can we convert string to interger.
    here s1=2 is string and s2=5 is interger
    now I want consolidated result like s1+s2=7
    is it possible?if possible then could you tell me how can i perform this operation.

  29. Posted April 21, 2015 at 8:42 am | Permalink

    Yes, I have found that you can convert a string with the ?number syntax as was suggested. You can < #assign s3=s1+s2> especially after processing s1?number into a different variable. What issue are you having?


  30. Kishor
    Posted July 7, 2015 at 10:01 pm | Permalink

    Hi Marty,

    I have one custom field with lot of text and i want to print it on Advance pdf layout.but when I disaplying only some content of text on page and another text is not what can i do?
    do you have any kind of solution..

    thanks in advance…

  31. Posted July 24, 2015 at 6:55 pm | Permalink

    Hello Kishor,

    You may want to do some checking by outputting the length of the text to make sure it all there. See this reference:

    We have noticed some funny references via CSS that you can tweak to get the display right.


  32. Posted August 9, 2016 at 12:36 pm | Permalink

    Have you had success in getting to a linked sublist. I am trying to access the item sublist on a transaction from a custom record through a parent field. I thought it would be

    originalrecord.parentfield.sublist but no such luck

  33. Brett F
    Posted August 10, 2016 at 8:06 pm | Permalink

    Hi Marty,

    I’m trying to create a template to be used for an email campaign.
    The Issue I’m coming across is that I cant get the “Dear [Customer Name]”, part of the merge to return a name from a Customer Contact record.

    Ideally we would like to be able to send a campaign to customer contact roles ie Director and then address the email to the related first name of the Contact.

    NetSuite support tells me that you can’t do this, but surely a function as basic as this for a mail merge should be possible within NetSuite.

    Having said this, I have managed to return some information from the $(} tag, however it returns too much information.
    ie. [CompanyID] [CompanyName] : [“Primary Contact” first & lastname]

    I have tried to use javascript to truncate the section before and including the :, however this has not been successful.

    This only seems to be available for the “Primary Contact” Role also, so it will not meet all of our needs…

    Can you help me please?

    Thanks in advance!

  34. Posted August 25, 2016 at 7:39 pm | Permalink

    Hello Mark,

    At this point in our use of NetSuite’s PDF generator, when we want any linked data, we use our Content Rendering Engine. It is so much easier to get to any related data using this tool.


  35. Posted August 25, 2016 at 7:52 pm | Permalink

    Hi Brett,

    Are you suggesting that you are having a hard time getting the JavaScript function to work? The trick here is to use a RegEx expression. See this article to get the last expression of the colon:


  36. kasi
    Posted August 30, 2016 at 1:47 am | Permalink

    Hi Marty,

    In Advance PDF how to show the total amount in words like for e.g my total is 4500 and In words Four Thousand five hundred only…

  37. Posted August 30, 2016 at 5:15 am | Permalink

    Hello Kasi,

    The way to add a function that displays numbers as words requires scripting. We actually provide this a standard feature in our Content Rendering Engine (CRE). Here is some samples of what it looks like.



  38. Posted September 7, 2016 at 11:21 am | Permalink

    Hi Marty,

    I need to display arabic text contents in the print layout. The text is saved in a netsuite field in the customer record. I can see the values properly when I open the customer record. But it is not getting rendered to the print layouts

  39. Posted September 7, 2016 at 11:28 am | Permalink

    What’s the output doing? Have you tried ?html See


  40. Posted September 8, 2016 at 1:16 am | Permalink

    To be specific, I have a customer requirement where he want to store item description in english as well as arabic. Then based on the requirement he would like to print invoices or quotes with english or arabic item descriptions. Customer name and address should also be printed in arabic if printing in arabic.

  41. Posted September 8, 2016 at 8:57 pm | Permalink

    Hello Prasun,

    My initial testing using our Content Rendering Engine to help see the data revealed the following:

    1. I updated the languages on an item using the Bulk Update Translation tool.
    2. When I modified the customer’s default language.
    3. When I added the item to a new Sales Order for that customer, it pulled over the description from the Bulk Language update.
    4. I was then able to reference the field as follows: record.item[0].description

    Still, this is basically using Native NetSuite to get your Arabic translated into transactions for you to reference by the tool. Hope this helps


  42. Posted September 22, 2016 at 6:49 am | Permalink

    Hey Marty,

    Running into an issue trying to import XML into another XML file created by BFO (pdf). This PDF/XML generates our packing slips.

    Here are the page declarations for the main packing slip, and the below is located in the header file:

    <!DOCTYPE pdf PUBLIC "-//" "report-1.1.dtd"


    The ENTITY boxtype is giving this error with that box_type.xml file:

    Error Parsing XML: The element type “link” must be terminated by the matching end-tag “”.

    That box_type.xml has this simple test right now:



    That code works if I hard code it on the xml file where I’m trying to import the file. But that’s not going to be scalable if box types are constantly changing! Especially since we have over ~45 individual packing slips for different companies.

    Also if I set the entity to:



    It will put that on the PDF that is generated – test print.

    Any ideas or am I doing something that really isn’t supported well in Netsuite (importing another XML into XML)? Any workarounds if it’s the later?

    Thanks in advance Marty!



  43. Posted September 25, 2016 at 7:57 pm | Permalink

    The key to getting this to work is to output the XML file to the folder system and then download it to open it with a text editor. Debug logs are HTML based and may fool you. Be sure to use an online XML validator to confirm it is good. Finally, have a look at this article about the ampersand as it gets all of us:

  44. Priyanka
    Posted March 29, 2017 at 10:52 pm | Permalink

    Hi Marty,

    Can you help me with the requirement where I need to implement Digital Signature in Advance PDF template. ?
    I refered the link:
    I used the code mentioned in this pdf with input tag. But its giving me error.

    1. Do we need to have separate module (BFO module) for this functionality.
    2. Whether it supports only specific versions.
    3. What should be my approach to achieve this functionality.

    Any help would be appreciated.


  45. Posted April 1, 2017 at 6:49 pm | Permalink

    Hi Priyanka,

    I do not believe all the BFO functions are supported; I don’t know about the feature you are trying to reference. NetSuite has chosen to implement the BFO features it desires. I found that it can be trial and error. Sometimes, I reach out to NetSuite Support to get to the bottom of these questions. Often times, I learn that the request turns into a future enhancement.


  46. Dhineshkumar
    Posted March 13, 2018 at 2:44 am | Permalink

    Hi Marty, how can we give current date in an adv PDF template?

  47. Posted March 17, 2018 at 1:38 pm | Permalink

    Try this:


    Here is the FreeMarker reference.

3 Trackbacks

  1. […] How To: Leverage NetSuite’s Advanced PDF / HTML Generation Tools […]

  2. […] wrote about the general framework about a year ago in this article:¬†“How To: Leverage NetSuite‚Äôs Advanced PDF / HTML Generation Tools”¬†. ¬†That article offers a good over and references to documents to help you build new […]

  3. […] How To: Leverage NetSuite‚Äôs Advanced PDF / HTML Generation Tools […]

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>