In this page


(since app version 6.4.0)

Better PDF Exporter allows rendering various formats of linear (1D) and matrix (2D) barcodes to the PDF documents exported from Jira. The supported barcode formats include all widely used ones like the ubiquitous QR code, UPC (Universal Product Code) and EAN (International Article Number). See the precise list in the next section.

You can use these machine-readable optical labels to encode URLs, product information, email addresses, geographic locations, custom identifiers and custom texts in your documents. This sample document (PDF version) contains a QR code link to the exported issue's details in Jira:

Barcodes can be recognized by not only special hardware devices (barcode readers), but also by free barcode scanner applications that are available on any smartphone platform! The mobile phone applications make barcodes an effective tool to insert easy-to-use links and identifiers in PDFs, especially when using printed media.

Example use cases:

  • Encode the URL of an issue in its PDF export. Then, your users can open the issue in the browser simply by pointing their smartphones' camera to the QR code.
  • Print and attach tags to physical objects that are represented by Jira issues. If you represent an asset like a physical server with a Jira issue, you can print and attach a tag to the server which can be scanned by staff members to open the corresponding issue.
  • When managing purchase requests with Jira issues, get the product code of the item from a Jira custom field, then insert its UPC or EAN barcode to the final document. This simplifies processing the purchase and eliminates errors at the same time.
  • Add the QR code of the Jira saved filter or ad-hoc JQL search to the document that captures the search results. Users can go and re-run the search for the most current results any time by using their QR code readers.
  • If you have a document type that exports only a part of the issue data, you can encode the link to the issue for those who need to see all issue details. For example, a story card will not contain the full description, but members of your Scrum team can scan the code and see the issue when standing in front of the physical board.

Supported barcode formats

  • 1D product
    1. UPC-A
    2. UPC-E
    3. EAN-8
    4. EAN-13
  • 1D industrial
    1. Code 39
    2. Code 93
    3. Code 128
    4. Codabar
    5. ITF
    6. RSS-14
    7. RSS-Expanded
  • 2D
    1. QR Code
    2. Data Matrix
    3. Aztec
    4. PDF 417
    5. MaxiCode

Generating barcodes

Generating barcodes in the default templates

The following default PDF templates support rendering QR codes out of the box:

  1. issue-fo.vm
  2. issue-navigator.vm
  3. story-cards.vm

When using these, you can activate QR codes via this variable in the template code:

## QR codes
#set($exportQrCodes = true) ## set to "true" to export QR codes
Automatic QR code sizing in issue-navigator-fo.vm

In case of issue-navigator-fo.vm, there is an interesting automatic switch between two modes. This template is typically used either with a saved filter or with an ad-hoc JQL search that is entered directly to Jira's JQL input box. The important difference between those is that saved filters use shorter URLs (which use only the filter ID as a request parameter) than ad-hoc searches (which use the full JQL string). Longer URL means longer data to encode in the QR code, which results in smaller black and white squares, which are harder to read.

issue-navigator-fo.vm intelligently recognizes these cases. If it finds the ad-hoc search case, it renders a larger QR code to effectively compensate the effect of the smaller squares. That way the QR code remains easy to read.

This type of adaptive sizing may be a technique to consider also when implementing QR codes in custom templates.

Generating barcodes in custom templates

You can easily add barcode and QR code rendering capabilities to any template.

First, execute the Groovy script which will make the barcode renderer object available to the template:


After this, you can access the barcode renderer via the simplistic API exposed by the $barcode object. $barcode offers a shortcut method that can be used in most cases, plus another one that also allows specifying the barcode resolution in pixels:

 * Returns the barcode in Base64-encoded PNG format, using the default resolution.
def generate(def content, def barcodeFormat)

 * Returns the barcode in Base64-encoded PNG format.
 * @param content the data to encode in the barcode (most typically a URL).
 * @param barcodeFormat barcode format to generate.
 * @param width image width in pixels.
 * @param height image height in pixels.
def generate(def content, def barcodeFormat, def width, def height)

The argument barcodeFormat is a string, one of the format names listed here. Note that different barcode formats support different length of content and different characters in the content. For example, while QR codes can encode any character, Code 11 only supports the digits (0-9), - and *. For the details of each format, we suggest checking its article in Wikipedia.

In the next sections, you can find working examples for various encoded data types and code formats. You can easily copy these to your own templates.

Encoding URLs in QR codes
## generate QR code to the passed URL in default resolution
	#set($qr = $barcode.generate("", "QR_CODE"))
	<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="1.5cm"/>

## generate QR code to a project's URL in default resolution
		#set($qr = $barcode.generate("${issue.projectObject.url}", "QR_CODE"))
		<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="1.5cm"/>

## generate QR code to a saved filter's URL in 120x120 resolution
		#set($qr = $barcode.generate("${requestContext.baseUrl}/issues/?filter=${}", "QR_CODE", 120, 120))
		<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="1.5cm"/>

## generate QR code to an ad-hoc JQL search in default resolution, but in 2.5cm x 2.5cm physical size
		#set($qr = $barcode.generate("${requestContext.baseUrl}/issues/?jql=$urlcodec.encode($searchRequest.query.queryString)", "QR_CODE"))
		<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="2.5cm"/>
Encoding product codes in barcodes
## generate a UPC-A barcode from the passed 12 digits in 100x40 resolution
## (the UPC-A in the example identifies the "iPhone 6s" product)
	#set($code = $barcode.generate("888462563048", "UPC_A", 100, 40))
	<fo:external-graphic src="url(data:image/png;base64,${code})" content-height="1.5cm"/>

## generate EAN-13 barcode from the passed 13 digits in 100x40 resolution
## (the EAN-13 in the example identifies the "OnePlus 5" product)
## (ISBN book identifiers are typically encoded as EAN-13, too)
	#set($code = $barcode.generate("6921815603627", "EAN_13", 100, 40))
	<fo:external-graphic src="url(data:image/png;base64,${code})" content-height="1.5cm"/>
Encoding email addresses in QR codes
## generate QR code to the passed email address in default resolution
## (barcode reader may offer sending an email to this address)
	#set($qr = $barcode.generate("", "QR_CODE"))
	<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="1.5cm"/>
Encoding geo URIs in QR codes
## generate QR code to the passed geographic location in default resolution
## latitude = 40.71872 deg N, longitude = 73.98905 deg W, altitude = 100
## (barcode reader may offer showing this in a map application)
	#set($qr = $barcode.generate("geo:40.71872,-73.98905,100", "QR_CODE"))
	<fo:external-graphic src="url(data:image/png;base64,${qr})" content-height="1.5cm"/>
Encoding custom text
## generate Code 128 barcode from the passed text in default resolution
	#set($code = $barcode.generate("Hello world", "CODE_128"))
	<fo:external-graphic src="url(data:image/png;base64,${code})" content-height="1.5cm"/>

## generate Code 128 barcode from the value in the 10101 custom field
	#set($code = $barcode.generate("${issue.getCustomFieldValue('customfield_10101')}", "CODE_128"))
	<fo:external-graphic src="url(data:image/png;base64,${code})" content-height="1.5cm"/>

Finding the optimal physical size and resolution for barcodes and QR codes

As you can see in the examples above, you can separately set the resolution (horizontal and vertical resolution in pixels) and the physical size (with and height in metric units) for the barcodes. The rule is relatively simple: for those barcode formats which support variable length data, the more data that needs to encoded, the more "complicated" the code becomes. For example, in case of the QR code, "more complicated" means smaller black and white squares.

To find the optimal values, simply test if your barcode reader can successfully read the code. If it can, set lower resolution and smaller physical size and try again. Repeat this until the reader has difficulties.

In our experience, barcodes are easier to read from paper than from an LCD screen! Therefore, if your primary media is paper, you can use lower physical size.

If you look for some formal recommendation, you can use the formulas recommended in this article.

The default templates are using sensible defaults, but feel free to change those in the template code if the codes are too small or too large for your use case.


You can learn more about using barcodes and QR codes in these articles:

  • The barcode Wikipedia article gives a great overview. It also has links to the articles of various barcode formats and to the technical specification (constraints on the encoded data) of those. Therefore it is a great starting point.
  • The QR code Wikipedia article is a great resource about QR code in general.
  • Better PDF Exporter is using the ZXing ("Zebra Crossing") open source library to implement barcode rendering. We strongly suggest to read the ZXing documentation if you need more flexibility by accessing ZXing in a lower level.
  • If you don't already use a code reader app, we recommend QR Code Reader for the Android platform. It is free and popular. Plus all the examples above were successfully tested with it.


Ask us any time.