In this page

Customizing PDF content, look and layout

Overview

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 AppsManage your appsPDF Templates 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.

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 $issue.resolutionObject.name) 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.
$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.name}, ${pdfView.description} and ${pdfView.templateName}.
$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.
$jql The JQL query as String that selects the issues to export. For single issue exports, it contains key=FOO-123 (with the actual issue key).
Available only if the PDF file is exported from the Issue Navigator.
$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))
	#end
#end
Velocity tools for templates

You can use the following tools in PDF templates:

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)
$commentManager CommentManager is used to retrieve the issue comments.

Groovy example:
def comments = commentManager.getCommentsForUser(issue, user)
$componentAccessor Can return references to $projectManager, $userManager 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)
## ...
$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.
$fieldVisibilityManager FieldVisibilityManager returns whether a custom field is visible for an issue.

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

Groovy example:
def linkCollection = issueLinkManager.getLinkCollection(issue, user)
$jqlSearch Searches for issues by exexuting a JQL query or a saved filter.

Velocity examples:
#set($issues = $jqlSearch.searchByJql("project = FOO"))
#set($issues = $jqlSearch.searchBySavedFilter(10000))
$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))
$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))
$userManager Returns users by account ID (or all users, although it should be avoided for efficiency reasons).

Velocity examples:
#set($users = $userManager.getUserByAccountId(1))
#set($users = $userManager.getUsersByAccountId([1, 2]))
#set($users = $userManager.getAllApplicationUsers())
$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))
$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)
$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"))
	<fo:block>$xmlutils.escape($issue.summary)</fo:block>
#end
$stringutils StringUtils offers a wide range of useful utility methods for strings.

Velocity example:
#set($headingLevel = $stringutils.countMatches($issue.summary, "."))
$pdfContent Get the list of comment objects:
#set($comments = $pdfContent.commentsByIssue($issue))

Get the list of change history objects:
#set($changeHistories = $pdfContent.changeHistoriesByIssue($issue))
$pdfFormatter Format file size values to the human-readable "3 Kb" format:
$pdfFormatter.formatFileSize($attachment.filesize)
Format duration values (in seconds) to the human-readable "1 day, 2 hour, 3 minutes" format, based on 24-hour calendar days:
$pdfFormatter.formatCalendarDuration($issue.timeSpent)
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):
$pdfFormatter.formatDuration($issue.timeSpent)
$pdfRenderer Transforms raw HTML markup to FO so it can appear correctly 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:
$scripting.execute("hello-world.groovy")
$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:
$userDateTimeFormatter.withStyle($dateTimeStyle.COMPLETE).format($currentDate)

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.

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('https://www.midori-global.com/images/midori_logo.gif')"/>

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 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, to access Jira internals (ex: getting project or version metadata) or to pre-process data before exporting that to PDF.

Questions?

Ask us any time.