In this page

Customizing PDF content, look and layout


PDF document content, look and page layout is defined by PDF template files named something-fo.vm. (There can be any number of these, but more on that later.)

The -fo.vm suffix denotes that these files are Velocity / FO templates. It means that those are a plain text file written in the Velocity template language, which is then transformed into FO documents, which is finally transformed to PDF. See more about this process in the next section.

Although both of those languages are very easy to grasp without any further reading, you may want to familiarize yourself with the basics of the Velocity template language syntax and the FO syntax. Where to start?

  1. You can probably learn most just by looking at the sample template files shipped with the app. Not only they are useful as is, they can also be great starting points to develop totally new templates.
  2. The best documentation available on the simple, yet powerful Velocity language is the Velocity User Guide. It is illustrated with great examples.
  3. Various XSL-FO tutorials are available on the web (just Google for "xsl fo tutorial"). Start with reading this tutorial if you want to pick up the FO basics quickly.

Editing PDF templates

PDF templates are editable through a convenient web based management interface, called the Template Manager. Go to AdministrationAdd-onsPDF Templates in Jira 6, and to AdministrationPDF Templates in Jira 5, to see the filelist. Please note that it includes not only templates, but also Groovy scripts and other resources available for the PDF renderer.

Click any of the filenames to edit it right in your browser! The editor allows modifying the filename and the description of the file, and edit its actual content. It comes with syntax highlighting, line numbering and other convenience features you would expect from a code editor.

It is also important to know that all these files are saved with the standard Jira backup mechanism, for the safety of your templates.

The PDF file rendering process

The two-stage PDF rendering process works like this:

  1. First, issue-fo.vm is rendered through Apache Velocity: it iterates over the issues, replaces the variable references (like $issue.description or $ by their actual values, executes scripts and so on. The result of this stage is a temporary in-memory FO document.
  2. Then, the resulted FO document is transformed into PDF by Apache FOP. The result is the actual final PDF document.

The rendering context

The rendering context is the set of variables and tools you can use in your PDF templates and in your Groovy scripts (optional, see this later). Every item in the context is identified by a unique name, just like a variable in a programming language. For example, the issues to be exported are available under the name $issues.

Velocity objects for templates

You can access the following domain objects while rendering the PDF template:

Domain Object Description
$ctx The Velocity context itself.
You will mostly need it when you want to include other templates using the $include tool.
$user ApplicationUser instance that represents the currently signed-in user, i.e. the user who started the PDF export.
(It is a User instance on Jira 6 and prior Jira versions.)
$pdfView Represents the "export type" which was selected by the user in the UI to start the PDF export.
You can access its public properties by the expressions ${}, ${pdfView.description} and ${pdfView.templateName}.
(since 3.0.0)
$pdfViewContext Represents the "export context" (the screen) which the PDF export was started from.
(since 8.2.0)
$title String title for the PDF document.
See the file properties page about working with titles.
$issues The Collection that stores the Issue objects to export. It contains only one item if the app was invoked from the Issue Details screen (single issue export), or multiple items when the app was invoked from the Issue Navigator (multi issue export).
You can iterate through this collection using the #foreach directive.
$searchRequest SearchRequest that is available only if the document is generated from a "search": browsing the issues with the Issue Navigator, executing filters, running free text searches, etc.
Not available for single issue exports.
$i18n I18nHelper instance that allows inserting internationalized Jira texts (like field names) to the PDF file content.
$requestContext.baseUrl Jira base URL as String.
$currentDate Date of the PDF file creation.

You are, of course, not limited to using these objects only. You can access tons of other information by navigating from these starting points in the object graph.

You can, for instance, retrieve the enclosing project and its latest version with the following Velocity code:

#if($issues.size() != 0)
	#set($project = $issues.get(0).projectObject)
	#if($project.versions.size() != 0)
		#set($lastVersionIndex = $project.versions.size() - 1)
		#set($version = $project.versions.get($lastVersionIndex))
Velocity tools for templates

First, you can use the tools that are available for Jira email templates also in PDF templates. These offer basic functionality for formatting, escaping and other common use cases.

You can additionally use:

Component / Tool Description
$changeHistoryManager ChangeHistoryManager returns the issue field value changes by various criteria.
Use this to export issue updates, workflow transition histories or metrics calculated from those.

Groovy example:
def changeHistories = changeHistoryManager.getChangeHistoriesForUser(issue, user)
$columnLayoutManager ColumnLayoutManager gives access to various Issue Navigator column layouts: a saved filter's or search's own column layout, the current user's own column layout, and the system default column layout.
This manager is particularly useful if you want to export an issue list, but reusing the column layout definition the user configured in Issue Navigator.
$commentManager CommentManager is used to retrieve the issue comments.

Groovy example:
def comments = commentManager.getCommentsForUser(issue, user)
$componentAccessor ComponentAccessor can return references to $projectManager, $userManager, $permissionManager and other Jira components that are not available in the Velocity context directly. You may want to use this when developing PDF templates and scripts that require components not listed in this table.

Velocity example:
#set($avatarService = $componentAccessor.avatarService)
#set($applicationProperties = $componentAccessor.applicationProperties)
#set($constantsManager = $componentAccessor.constantsManager)
#set($projectManager = $componentAccessor.projectManager)
#set($userManager = $componentAccessor.userManager)
## ...
$componentManager ComponentManager is the deprecated (legacy) way to get references to Jira core components.
You should normally use $componentAccessor (see the previous item), and revert to using this only in Jira versions prior to 5.2. It is completely removed in app version 6.3.0.
$customFieldManager CustomFieldManager supports working with custom fields.

Velocity example:
#set($customFields = $customFieldManager.getCustomFieldObjects($issue))
$fieldScreenRendererFactory FieldScreenRendererFactory allows you to obtain field screen renderers to check which custom fields are added to what screens and tabs.
(since app version 3.3.0)
$fieldVisibilityManager FieldVisibilityManager returns whether a custom field is visible for an issue.

Velocity example:
#if($fieldVisibilityManager.isFieldVisible('versions', $issue)) ... #end
(since app version 3.3.0)
$issueLinkManager IssueLinkManager can return the inward / outward issue links (for example, "duplicated by" and "duplicates").

Groovy example:
def linkCollection = issueLinkManager.getLinkCollection(issue, user)
$issueViewUtil IssueViewUtil is legacy collection of utility methods.
All functionality offered by this is now available via "smarter" objects in the Velocity context.
$jiraDurationUtils JiraDurationUtils export nice time duration strings in long ("2 days 5 hours") or compact format ("2d 5h").
$remoteIssueLinkManager RemoteIssueLinkManager gives access to the links between Jira issues and remote objects in remote applications (most typically pages in a Confluence instance).

Velocity example:
#set($remoteIssueLinks = $remoteIssueLinkManager.getRemoteIssueLinksForIssue($issue))
(since app version 3.4.0)
$subTaskManager SubTaskManager manages sub-tasks (issues that are "part of" other issues).
This is useful if you want to do complex work with sub-tasks, more than just getting the sub-task of an issue or the parent of a sub-task, both of which can be done with the getters of the $issue itself.

Velocity example:
#set($subTaskIssueLinks = $subTaskManager.getSubTaskIssueLinks($issue))
(since app version 10.1.0)
$tableLayoutFactory TableLayoutFactory helps to construct custom column layouts.
This is useful if you want to have a dynamically changing column layout in your PDF documents, identical with the one you currently use see in the Issue Navigator.
$thumbnailManager ThumbnailManager can return thumbnail versions of image attachments.
This is useful if you want to display the smaller version of image attachments in the PDF document.

Velocity example:
#set($thumbnails = $thumbnailManager.getThumbnails($issue, $user))
$worklogManager WorklogManager gives access to the "logged work" records.
You will use it if you need to process or export work log information.

Velocity example:
#set($worklogs = $worklogManager.getByIssue($issue))
$workRatio WorkRatio calculates Jira's special "work ratio" metric.

Velocity example:
#set($percentage = $workRatio.getWorkRatio($issue))
(since app version 3.3.0)
$date DateTool is the tool for free date and time formatting.
It should only be used in case of very specific formatting requirements. Otherwise see $userDateTimeFormatter.

Velocity example:
$date.format("yyyy-'W'ww-EEE", $currentDate)
(since app version 3.3.0)
$dateFormatter DateTimeFormatter is Jira's built-in date formatter.
There is a preferred shorthand available: see $userDateTimeFormatter.

Velocity example:
(since app version 3.3.0)
$include Helps to include one template in another.
It promotes the good practice of externalizing the reusable parts of your templates, and use those as "include snippets" in multiple concrete templates.

Velocity example:
$include.parse($ctx, "your-company-header-fo.vm")
$math MathTool is to perform basic floating point arithmetic operations in Velocity.
In case you do lots of calculations, we strongly suggest implementing those in Groovy. Velocity is primarily a template language, thus it is not the best fit for this purpose.

Velocity example:
#set($totalTimeSpent = $math.add($totalTimeSpent, $worklog.timeSpent))
$number NumberTool is to format Number objects as integer, floating point, percent or currency.

Velocity example:
$number.format("integer", $storyPoints)
$sorter SortTool is to sort collections by any property (or properties) of the contained items.
It is super-userful for basic sorting. For more complex sorting, use Groovy comparators.

Velocity example:
#foreach($issue in $sorter.sort($issues, "key"))
(since app version 5.9.0)
$stringutils StringUtils offers a wide range of useful utility methods for strings.

Velocity example:
#set($headingLevel = $stringutils.countMatches($issue.summary, "."))
$gadget Exports Jira dashboards, gadgets and reports to PDF.
See the dashboard exporting tutorial for details.
$pdfContent Get the list of comment objects:
#set($comments = $pdfContent.commentsByIssue($issue))

Get the list of change history objects:
#set($changeHistories = $pdfContent.changeHistoriesByIssue($issue))
$pdfField Renders field values in the HTML format how those would be displayed in the Jira web UI.
Remember that using this tool is a resource-consuming way to obtain field values. Also, field values will always be returned as String objects, losing their original low-level data types (e.g. Date). Due to these reasons, this tool is rarely needed, but it will be immensely useful when working with the HTML presentation is the only way.

Get the HTML rendered value of a field by passing the field ID:
$pdfField.getValueAsHtml($issue, "customfield_10123")

Or by passing the field object itself if you have that:
$pdfField.getValueAsHtml($issue, $customField)

Note that in many cases, you want to convert the resulted HTML to FO, so that the field value appears in the PDF the way it is displayed in the web UI. For that, use this:
$pdfRenderer.htmlToFo($pdfField.getValueAsHtml($issue, $customField))
(since app version 7.4.0)
$pdfFormatter Format file size values to the human-readable "3 Kb" format:
Format duration values (in seconds) to the human-readable "1 day, 2 hour, 3 minutes" format, based on 24-hour calendar days:
Format duration values (in seconds) to the human-readable "1 day, 2 hour, 3 minutes" format, based on working days (which are configurable in Jira, e.g. 8 hours per working day):
$pdfRenderer Renders the text type fields that rely on Jira's wiki style renderer in a way that they preserve their formatting (bold, italic, lists, tables, images, etc.) also in the resulted PDF document!

Velocity example:
$pdfRenderer.asRendered($issue, 'description', $issue.description)
$scripting Executes Groovy scripts to implement advanced logic and visualization (ex: charts) in templates.
Make sure you read about executing Groovy scripts.

Velocity example:
$userDateTimeFormatter This DateTimeFormatter instance, using the currently signed-in user's preferences, should be the primary tool for date and time formatting.
If you have custom requirements that cannot be met with this, see $date.

Velocity example:
(since app version 5.9.0)

Expressions for templates

These are the best resources to find the template language expressions for your own templates:

  1. The Expressions Reference Manual gives you categorized expressions for all the frequent needs, that you can just copy to your own templates.
  2. The code of the default templates shipped with the app are also worth a deeper look. We offer several templates built for real-life use cases, so make sure to check the default templates that export the same data or work similar to your own templates.
  3. To better understand the data model and the possibilities, study the Jira domain model documentation. You will learn which class offers what properties and what methods, and their general responsibilities.

Encoding in international templates

You are writing your template text directly to the fo.vm files which are actually plain text documents stored in the file system. You might encounter hard to track encoding problems, because your text editor and your file system must encode the text files properly.

To prevent these potential problems, there is an easy trick: since the fo.vm files are XML files, you can encode your template text characters into NCRs. With this, encoding doesn't matter anymore!

Using this web based converter tool you can:

  1. paste your template text to the Characters: box
  2. click Convert above Characters:
  3. copy the converted string from the Hexadecimal NCRs: box to your text editor. (These are just hex characters, so there will be no problem.)

Embedding images and issue attachments in the PDFs

Embedding external images

In short: images are fully supported. You will use the <fo:external-graphic> element, which supports every sort of resizing options.

This code snippet will give you an idea, or will even do the job for you in most of the cases:

<fo:external-graphic width="100%" height="100%" content-width="scale-to-fit" content-height="100%" src="url('')"/>

For more information about the parameters, please see the tons of great tutorials available on the web.

Embedding images attached to issues

The default issue-fo.vm template differentiates between image attachments and non-image attachments. For the former, it displays the thumbnails. For the latter, it simply prints the filenames and file sizes.

In certain applications, you might rather want the image thumbnails not to be shown in the PDF. This is easy: just set the $exportThumbnails option to false in the template.

Embedding all types of files attached to issues

In some cases, you may want to embed all the files attached to the exported issues in the PDF, not only the images.

This is also supported! It makes your PDFs real self-containing units of information. Please read the embedding issue attachments page for details.

Implementing custom logic with Groovy scripting

Some document types need more logic than just simple if-then statements. You may want to, for instance:

  • Draw charts. (Ex: generate graphical visualization in project status reports.)
  • Integrate with external resources. (Ex: integrate vendor information into your quotes queried from an external CRM database or an external webservice.)
  • Do precise arithmetic. (Ex: calculate precise money values in invoice documents.)
  • Access Jira internals. (Ex: execute a secondary saved filter to collect more data.)
  • Implement data processing algorithms using advanced data structures. (Ex: build dependency tables for traceability matrixes.)

To implement these you can easily write Groovy scripts and execute those scripts while generating the final PDF document! Please read the scripting to implement custom logic page.

Rendering charts

Charts are visual representations of Jira data. Charts often make it easier to understand the data in business reports, because readers can easily pick out patterns and trends illustrated in the charts that are otherwise difficult to see when looking at raw numbers.

A detailed, yet easy to follow tutorial that will explains how to collect data for charts, how to customize the look of charts and how to insert them to the final PDF documents is available here.

Next step

Run short Groovy scripts to integrate data from external resources (external database or API), to access Jira internals (ex: getting project or version metadata) or to pre-process data before exporting that to PDF.


Ask us any time.