Documentation page: PD4ML JSP Tag Library. This page is also available as Markdown at pd4ml-jsp-taglib.md.

PD4ML JSP Tag Library

The PD4ML JSP tag library converts the output of a JSP page to PDF, PDF/A, PDF/UA, DOCX, RTF or an image without a line of Java: wrap the page's HTML in <pd4tl:transform>, and the browser receives the document instead of the HTML. Further tags add page headers and footers, passwords and permissions, TrueType fonts, document metadata, dynamic placeholders, and saving the result on the server.

The tags are part of the PD4ML jar, for both servlet containers in use today: the classic javax.servlet ones (Tomcat 9 and earlier) and Jakarta EE (Tomcat 10 and later). They use the PD4ML Java API described in the PD4ML Programmer's Manual; this guide covers tag library version 1.2.

Contents


1. Setup

1.1 Libraries

Put the PD4ML jar into the web application's WEB-INF/lib (or the container's shared library directory), together with the Bouncy Castle jar PD4ML ships with, bouncycastle-runtime.jar (see Obtaining PD4ML). With Maven, the com.pd4ml:pd4ml dependency brings both.

Nothing has to be added to web.xml: the tag library descriptors are in the jar's META-INF directory, where JSP containers find them by their URI. The only exception is the optional forwarder servlet (3.5).

1.2 The taglib directive

Container Directive
Jakarta EE 9 and later: Tomcat 10+, Jetty 11+, WildFly 27+... <%@ taglib uri="http://pd4ml.com/tlds/4.2-jk" prefix="pd4tl" %>
Java EE / javax.servlet: Tomcat 9 and earlier... <%@ taglib uri="http://pd4ml.com/tlds/4.2" prefix="pd4tl" %>

The URI must be written exactly as above (http://, not https://): containers match it as a string. Using the URI of the other servlet flavour fails with a class-not-found error when the page is compiled.

Use the prefix pd4tl, not pd4ml. PD4ML's own extension tags in the converted HTML (<pd4ml:page.break>, <pd4ml:page.header>, <pd4ml:toc>...) use the pd4ml: prefix. If the tag library is imported with the same prefix, the JSP compiler takes those tags for tag library tags and rejects the page ("No tag [page.break] defined in tag library imported with prefix [pd4ml]"). With pd4tl, both work side by side.

1.3 License

The tags create their PD4ML converter with the default constructor, which looks for the license file pd4ml.lic in the working directory of the container's JVM and then on the class path. In a web application, put it into WEB-INF/classes/pd4ml.lic. Without a license, PD4ML runs in evaluation mode and marks every page (see Licensing); PDF/A, PDF/UA, DOCX/RTF and image output need a license that covers them.

2. Quick start

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://pd4ml.com/tlds/4.2-jk" prefix="pd4tl" %>
<pd4tl:transform screenWidth="800" pageFormat="A4" pageInsets="15,15,15,15,mm">
<html>
<head>
  <title>Invoice <%= request.getParameter("id") %></title>
  <style>body { font-family: sans-serif } h1 { color: #036 }</style>
</head>
<body>
  <h1>Invoice <%= request.getParameter("id") %></h1>
  <p>Generated on <%= new java.util.Date() %>.</p>
</body>
</html>
</pd4tl:transform>

Requesting the page returns an A4 PDF (application/pdf, shown in the browser). Everything inside <pd4tl:transform> is ordinary JSP: scriptlets, EL, JSTL, includes. Only the HTML it produces is converted.

screenWidth is required: it is the width, in pixels, of the virtual browser window the HTML is laid out in, and it sets the scale of the page (see 7. Page layout in the manual). Set it to the width the HTML was designed for.

3. How it works

3.1 Request processing

  1. When <pd4tl:transform> starts, it resets the response: whatever the page wrote or set before it (content, headers, cookies) is discarded, and the document's headers are set: Content-Type for the chosen output format, Content-disposition (inline or attachment, with the file name), and cache headers.
  2. The tag's body is evaluated as usual, but its output is collected instead of being sent. Tags such as <pd4tl:header> or <pd4tl:permissions> inside it record their settings.
  3. When the tag ends, PD4ML converts the collected HTML with these settings and writes the document to the response (or saves it, or redirects - see savefile and forward).
  4. The rest of the page after </pd4tl:transform> is not processed.

Because the response is reset, white space or other output before the tag does no harm, as long as the response has not been committed yet (by flushing, or by writing more than the page buffer, 8 KB by default). For the same reason, the page cannot add its own response headers.

3.2 Images, style sheets and other resources

Relative references in the HTML (<img src="images/logo.png">, <link href="../css/print.css">) are resolved against the JSP file's location on disk, so files next to the JSP are read directly. If the web application is not unpacked (no file system path is available), they are resolved against the request URL and loaded over HTTP from the server itself.

Server-absolute paths (/myapp/images/logo.png) are not resolved automatically. Use absolute URLs, or let <pd4tl:fix_base_path> prefix them with the server address.

When PD4ML loads resources over HTTP, it appends the current session id (;jsessionid=...) to the URLs, so that pages and images behind a login are loaded within the user's session. To keep the session id away from other hosts, switch this off: <pd4tl:property name="pd4ml.sessionid.append" value="false"/>.

3.3 Layout engine

The tag library uses PD4ML's established v40 layout engine. The v42 engine is currently available through the Java API only.

3.4 Output formats

outputFormat chooses what the tag produces:

Value Output Content-Type
(not set), pdf PDF 1.7 application/pdf
pdfa PDF/A-1b application/pdf
pdfua PDF/UA-1 combined with PDF/A-2a (tagged PDF) application/pdf
docx Microsoft Word document application/vnd.openxmlformats-officedocument.wordprocessingml.document
rtf RTF, images in their original format application/rtf
rtfwmf RTF, images converted to WMF (for WordPad) application/rtf
png24 PNG image of the first page, with transparency image/png
png8 PNG image of the first page, without transparency image/png
tiff, tiffcomp TIFF image of the first page, uncompressed or compressed (PackBits) image/tiff
mpagetiff, mpagetiffcomp TIFF with all pages, uncompressed or compressed image/tiff

The value is not case-sensitive. The image formats show the first page of the document; combine them with outputRange to render another page. TIFF output needs Java 9 or later.

Set fileName to match the format (fileName="report.docx"): it defaults to file.pdf whatever the format is.

3.5 Forward mode (PD4ForwarderServlet)

In some environments - frameworks that write the page through response.getWriter(), or pages that include other content - writing the document from the JSP fails with an IllegalStateException. For those cases the tag can hand the delivery over to a servlet: with forward="...", the tag stores the conversion request in the HTTP session and redirects the browser to PD4ForwarderServlet, which produces and sends the document.

Register the servlet in web.xml:

<servlet>
  <servlet-name>pd4forward</servlet-name>
  <!-- javax containers: com.pd4ml.taglib.javax.PD4ForwarderServlet -->
  <servlet-class>com.pd4ml.taglib.jakarta.PD4ForwarderServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>pd4forward</servlet-name>
  <url-pattern>/pd4forward</url-pattern>
</servlet-mapping>

and point the tag to it:

<pd4tl:transform screenWidth="800" fileName="report.pdf"
                 forward='<%= request.getContextPath() + "/pd4forward" %>'>
...
</pd4tl:transform>

The redirect needs the browser's session (cookies, or URL rewriting). The stored request stays in the session until the session expires, so for large documents prefer the direct mode where possible. A forward path that starts with / is relative to the server, not to the application: include request.getContextPath() as above.

4. Tag reference

All attributes accept request-time values (<%= ... %> expressions and ${...} EL). Values that are switches take "true" (not case-sensitive) for on; any other value means off.

The tags other than transform, fix_base_path, clean_xhtml and resolve_entities must be nested inside <pd4tl:transform>; they can appear anywhere in its body, before or after the HTML, and produce no output of their own. Outside a transform they have no effect.

4.1 pd4tl:transform

Converts its body (or the page given by url) and sends the result.

Page layout

Attribute Value Default
screenWidth Required. Width of the virtual browser window, in pixels
pageFormat A page size name: A0...A10, B4, B5, ISOB0...ISOB5, JIS_B4, JIS_B5, LETTER, HALFLETTER, LEGAL, LEDGER, TABLOID, NOTE, ArchA...ArchE, ArchE1. Case-sensitive (a4 is an error). Or a custom size "<width>x<height>" in pixels, at 1.35 px per point ("810x1215" is 600×900 pt) A4
pageOrientation landscape or portrait portrait
pageInsets Page margins "top,left,bottom,right,units": whole numbers, units mm or points 10,20,30,40,mm
adjustScreenWidth true: widen the layout to the content's actual width, if it is wider than screenWidth (so that wide content is not cut off) false
fitPageVertically true: scale the whole document down to a single page false
multicolumn Multi-column pages: "<columns>,<gap>" with the gap in mm or pt, e.g. "2,10mm" or "3,25pt" (no spaces). Not combined with adjustScreenWidth or fitPageVertically
protectPhysicalUnitDimensions true: sizes in physical units (mm, cm, in, pt) keep their size on paper instead of being scaled with the layout false
outputRange The pages to output: "1", "2+" (all from the second), "1-3", "odd", "even", "skiplast", or combinations like "3-7,odd" all
initialPageNumber Number of the first page, for $[page] and $[total] 1

The default margins are deliberately not symmetric (top 10, left 20, bottom 30, right 40 mm): set pageInsets explicitly in most documents.

Output

Attribute Value Default
outputFormat See 3.4 pdf
pdfa true: PDF/A-1b output. Takes precedence over outputFormat false
fileName File name offered to the browser. Spaces are replaced with _ file.pdf
inline true: show the document in the browser; false: offer it as a download true
http noheader: do not set the Content-Type and cache headers
forward URL of PD4ForwarderServlet, see 3.5
enable false: do not convert; the body is sent as HTML (useful to check the generated HTML). The rest of the page after the tag is skipped in this case too true

Document content

Attribute Value Default
documentTitle Document title in the PDF properties; replaces the HTML <title> the HTML <title>
authorName Author in the PDF properties
outline PDF bookmarks: headings (from <h1>...<h6>), anchors (from named anchors) or none none
generateHyperlinks false: do not turn HTML links into PDF links true
generatePdfForms true: turn HTML form controls into PDF form fields false
formFont Font family of the PDF form field values; a TrueType font made available with <pd4tl:usettf> or @font-face
kerning true: apply the fonts' kerning false
ligatures true: use the ligature glyphs ("fi", "fl"...) of TrueType/OpenType fonts false

Source and loading

Attribute Value Default
url Convert this page instead of the tag's body (the body is then ignored). An absolute http:// or https:// URL, or a path starting with /, which is completed with the current server address - without the application's context path: use '<%= request.getContextPath() + "/report.jsp" %>'
proxy HTTP proxy "host:port" for loading url and the document's resources
basicAuthentication "login:password" for HTTP Basic authentication when loading url and the document's resources
encoding Leave unset (the body is converted as text, its encoding is irrelevant)
debug true or a PD4ML log level: log the conversion, including resource loading, to the server's console output
useAFM Obsolete, has no effect

4.2 pd4tl:header and pd4tl:footer

Page header and page footer. Each can be written in one of two ways.

With an HTML body (recommended): the tag's body is the header's HTML, with the placeholders $[page], $[total] and $[title]. Style it with CSS; JSP expressions in it are evaluated first.

<pd4tl:header>
  <div style="font-size: 9pt; border-bottom: 1px solid #999">
    <b>$[title]</b> <span style="float: right">Page $[page] of $[total]</span>
  </div>
</pd4tl:header>

With attributes (the PD4ML v3 way): a title and a page number, each aligned left, center or right.

<pd4tl:footer areaHeight="24" fontSize="9" color="#666666"
              titleTemplate="Confidential" titleAlignment="left"
              pageNumberTemplate="Page $[page] of $[total]" pageNumberAlignment="right"/>
Attribute Value Default
titleTemplate Text with the placeholders $[title], $[page], $[total] (HTML markup allowed)
titleAlignment left, center or right center
pageNumberTemplate Text with the placeholders $[page], $[total]
pageNumberAlignment left, center or right right
fontSize Text size (CSS pixels of the layout) 10
fontFace Font family Courier New
color Text color, #rrggbb black
areaHeight Height of the header or footer area (CSS pixels of the layout). 0: no header/footer text, only the watermark and background below 12
pagesToSkip Number of pages at the beginning without the header or footer 0
initialPageNumber Number of the first page (applies to the whole document; the transform attribute of the same name takes precedence) 1
watermarkUrl Image to show on every page, as an absolute URL
watermarkBounds Position and size of the watermark image, "x,y,width,height"; required with watermarkUrl
watermarkOpacity Watermark opacity, 0...100 100
pageBackgroundColor Page background color, #rrggbb
pageBackgroundImageUrl Page background image (a letterhead or form), stretched to the page

With an HTML body, the text attributes (titleTemplate, pageNumberTemplate, the alignments, fontFace, fontSize, fontStyle, color) are not used, and the area takes the height of its content. fontStyle (plain, bold, italic, bolditalic) is accepted for compatibility but does not change the text: use the HTML body for bold or italic text. The watermark and the page background appear on every page, pagesToSkip does not apply to them.

Headers and footers can also be defined in the HTML itself, with <pd4ml:page.header> and <pd4ml:page.footer> - see Headers, footers, watermarks and backgrounds.

4.3 pd4tl:permissions

Encrypts the PDF and sets what its users may do.

Attribute Value
password Required. Password to open (and change) the document. empty (or an empty value): no password to open it, but the rights still apply
rights Required. The permitted operations: names separated with ,, + or spaces - print, degradedprint, modify, copy, annotate, fillingforms, contentextraction, assembly (the Allow prefix of the Constants is optional: "AllowPrint+AllowCopy" works too) - or their numeric sum (2068 = print and copy, -1 = everything)
encryption rc4-40, rc4-128, aes-128 or aes-256. Default: aes-128
<pd4tl:permissions password="s3cret" rights="print,copy" encryption="aes-256"/>

PDF/A documents cannot be encrypted: with pdfa or outputFormat="pdfa" the permissions are ignored.

4.4 pd4tl:usettf

Makes a directory of TrueType/OpenType fonts available to the document and embeds the fonts it uses.

Attribute Value Default
from Required. The font directory: a path on the server, a file: or http(s): URL, or java: and a class path location (java:fonts for WEB-INF/classes/fonts)
embed false: reference the fonts by name without embedding them (they must then be installed on the reader's computer) true
allGlyphs true: embed the complete fonts instead of the glyphs used false
<pd4tl:usettf from='<%= application.getRealPath("/WEB-INF/fonts") %>'/>

The directory needs a font index, pd4fonts.properties. If it has none, PD4ML creates it there on first use, which requires write access to the directory and takes a moment for large directories: for production, ship the index with the fonts (java -jar pd4ml.jar -configure.fonts <dir>, see Font indexing). A relative path is resolved against the JVM's working directory, so prefer application.getRealPath(...) or java:.

4.5 pd4tl:savefile

Saves the document on the server, and optionally redirects the browser instead of sending it.

Attribute Value Default
dir Required. Directory to save the document in. It must exist
name File name. If a file of that name exists, a name with a number added is chosen (report1234567.pdf) pd4<number>.pdf
redirect disabled: send the document to the browser as well. pdf: redirect the browser to the saved file (requires uri). A URL: redirect to it, with the saved file's name as parameter filename disabled
uri The URL under which dir is reachable; required with redirect="pdf"
redirectMethod redirect: an HTTP redirect (the browser loads the new URL). forward: a server-side forward within the application redirect
debug true: log the file operations to the console (and create a missing directory)
<pd4tl:transform screenWidth="800">
  <pd4tl:savefile dir='<%= application.getRealPath("/archive") %>'
                  redirect='<%= request.getContextPath() + "/saved.jsp" %>'/>
  ...
</pd4tl:transform>

saved.jsp then receives the file name: <%= request.getParameter("filename") %>.

Notes:

  • With redirectMethod="redirect" (the default), a URL starting with / is relative to the server: include request.getContextPath(). With forward, it is relative to the application.
  • The automatic name ends in .pdf whatever the output format: set name for other formats.
  • name is used as a path below dir: never pass user input to it unchecked.

4.6 pd4tl:parameters and pd4tl:parameter

Values for placeholders $[name] in the HTML. Useful for content that is written in a static template but known only at request time.

<pd4tl:parameters>
  <pd4tl:parameter key="customer" value="ACME Corp"/>
  <pd4tl:parameter key="order" value='<%= request.getParameter("order") %>'/>
</pd4tl:parameters>
...
<p>Order $[order] for $[customer]</p>
Tag Attributes
parameters none; contains the parameter tags. Use one parameters block per page
parameter key (required): the placeholder name. value (required): its value

The values are inserted as text (HTML markup in them is shown, not interpreted). A parameter whose value is null is left out, and its placeholder then appears literally in the document. The names page, total and title are reserved for headers and footers. See also setDynamicData() in the manual.

4.7 pd4tl:metadata

A custom metadata property of the PDF (XMP), for example a document number for an archive.

Attribute Value
key Required. Property name: a valid XML element name (not one of the standard properties Title, Author, Subject, Keywords, Creator, Producer, CreationDate, ModDate, Trapped)
value Required. Property value
hidden true: store the property in PD4ML's own namespace, which PDF viewers do not show. Default: shown among the document's custom properties
<pd4tl:metadata key="DocumentId" value='<%= invoice.getNumber() %>'/>

4.8 pd4tl:property

Sets one of PD4ML's advanced conversion parameters (see setParam() and the PD4ML_... constants).

Attribute Value
name Required. Parameter name
value Required. Parameter value

Useful ones:

name value Effect
pd4ml.sessionid.append false Do not append the session id to resource URLs (3.2)
pd4ml.document.view.mode e.g. TwoColumnLeft, SinglePage Initial page layout in PDF viewers
pd4ml.allowed.resource.location a URL prefix Load resources from this location only (for HTML from untrusted sources)
pd4ml.disable.external.attachments true Ignore attachments that refer to external files
pd4ml.html.in.dynamic.data true Interpret HTML in <pd4tl:parameter> values
pd4ml.media.type.print true Apply @media print style sheet rules
pd4ml.socket.timeout milliseconds Timeout for loading resources
pd4ml.document.id a string The PDF's document identifier

Unknown names are ignored.

4.9 pd4tl:fix_base_path

Rewrites server-absolute paths in its body into full URLs that PD4ML can load. Can be used inside or outside transform.

Attribute Value Default
pattern Required. The path prefix to complete, e.g. /myapp/images/
basePath What to put in front of it, e.g. https://cdn.example.com the current server: http(s)://host:port
addSessionID true: add ;jsessionid=... to the paths (write the attribute values in double quotes) false
debug true: log the body before and after to the console
<pd4tl:fix_base_path pattern="/myapp/images/">
  <img src="/myapp/images/chart.png">
</pd4tl:fix_base_path>

Every occurrence of pattern in the body is completed - in attributes and in text alike - so choose a pattern that occurs only in the paths.

4.10 pd4tl:clean_xhtml and pd4tl:resolve_entities

Two filters for their body, kept for compatibility with older pages:

  • clean_xhtml turns XHTML-style empty elements (<br/>) into HTML (<br>) by replacing every /> in the body - including any in text or attribute values. PD4ML reads XHTML directly, so this is rarely needed.
  • resolve_entities replaces numeric character references (&#1055;, &#x440;) with the characters, which helped right-to-left scripts in older versions. It also turns escaped markup (&#60;) into real markup, so do not apply it to user-supplied text, and it does not handle characters beyond the Basic Multilingual Plane (emoji).

5. Use cases

The examples use the Jakarta URI; on javax containers use http://pd4ml.com/tlds/4.2.

5.1 A report as a download, landscape Letter

<%@ taglib uri="http://pd4ml.com/tlds/4.2-jk" prefix="pd4tl" %>
<pd4tl:transform screenWidth="1000" pageFormat="LETTER" pageOrientation="landscape"
                 pageInsets="36,36,36,36,points" outline="headings"
                 fileName="Quarterly Report.pdf" inline="false">
<html>
<head><title>Quarterly Report</title></head>
<body>
  <h1>Quarterly report</h1>
  <jsp:include page="/WEB-INF/report/table.jsp"/>
</body>
</html>
</pd4tl:transform>

The browser saves Quarterly_Report.pdf, with bookmarks from the headings.

5.2 Headers, footers and a watermark

<% String base = request.getScheme() + "://" + request.getServerName() + ":"
               + request.getServerPort() + request.getContextPath(); %>
<pd4tl:transform screenWidth="800" pageInsets="20,15,20,15,mm">
  <pd4tl:header pagesToSkip="1">
    <div style="font-size: 8pt; color: #666">$[title]</div>
  </pd4tl:header>
  <pd4tl:footer>
    <div style="font-size: 8pt; text-align: right">Page $[page] of $[total]</div>
  </pd4tl:footer>
  <pd4tl:header areaHeight="0" watermarkUrl='<%= base + "/images/draft.png" %>'
                watermarkBounds="150,300,300,300" watermarkOpacity="20"/>
<html><head><title>Contract draft</title></head><body>...</body></html>
</pd4tl:transform>

5.3 Archive copy as PDF/A with embedded fonts

<pd4tl:transform screenWidth="800" outputFormat="pdfa" fileName="invoice-1042.pdf">
  <pd4tl:usettf from="java:fonts"/>
  <pd4tl:metadata key="InvoiceNumber" value="1042"/>
<html><head><title>Invoice 1042</title></head>
<body style="font-family: 'DejaVu Sans'">...</body></html>
</pd4tl:transform>

PDF/A requires all fonts to be embedded: provide them with <pd4tl:usettf> (here from WEB-INF/classes/fonts) and use them in the CSS. For accessible documents, use outputFormat="pdfua" with well-structured HTML (headings, lang, alt texts) - see PDF/A, PDF/UA and e-invoicing.

5.4 A protected document

<pd4tl:transform screenWidth="800">
  <pd4tl:permissions password="empty" rights="print"/>
<html><body><p>Can be printed, but not copied or changed.</p></body></html>
</pd4tl:transform>

5.5 Save on the server and show a confirmation page

<pd4tl:transform screenWidth="800">
  <pd4tl:savefile dir="/var/app/statements" name="statement.pdf"
                  redirect="/confirm.jsp" redirectMethod="forward"/>
<html><body>...</body></html>
</pd4tl:transform>

confirm.jsp:

<p>Your statement was saved as <%= request.getParameter("filename") %>.</p>

5.6 Converting another page or an external site

<%-- a page of this application --%>
<pd4tl:transform screenWidth="1000" url='<%= request.getContextPath() + "/catalog.jsp" %>'/>

<%-- an intranet page behind a proxy and Basic authentication --%>
<pd4tl:transform screenWidth="1200" url="https://intranet.example.com/status.html"
                 proxy="proxy.example.com:3128" basicAuthentication="reporter:secret">
  <pd4tl:property name="pd4ml.sessionid.append" value="false"/>
</pd4tl:transform>

5.7 Word document and page thumbnail

<pd4tl:transform screenWidth="800" outputFormat="docx" fileName="letter.docx" inline="false">
<html><body><h1>Letter</h1><p>Dear customer, ...</p></body></html>
</pd4tl:transform>

<pd4tl:transform screenWidth="800" outputFormat="png24" outputRange="2" fileName="page2.png">
...
</pd4tl:transform>

5.8 Placeholders filled at request time

<pd4tl:transform screenWidth="800">
  <pd4tl:parameters>
    <pd4tl:parameter key="customer" value='<%= user.getCompany() %>'/>
  </pd4tl:parameters>
  <jsp:include page="/WEB-INF/templates/offer.html"/>
</pd4tl:transform>

offer.html contains $[customer] wherever the customer's name belongs.

6. Tips and gotchas

  • Nothing after </pd4tl:transform> runs. Put all logic before or inside the tag.
  • The response is reset when the tag starts: headers or cookies the page set before are lost. Set cookies in a filter or another request; the tag sets the document's own headers.
  • Use prefix="pd4tl", so that PD4ML's in-HTML tags (<pd4ml:page.break/>...) can be used in the page.
  • Paths starting with / in url, forward and savefile's redirect are relative to the server root: add request.getContextPath().
  • Set pageInsets and fileName explicitly: the defaults (10/20/30/40 mm, file.pdf) rarely fit.
  • pageFormat names are case-sensitive (A4, LETTER, ArchA); numbers in pageInsets, watermarkBounds and multicolumn must be whole numbers.
  • Create the savefile directory in advance, and check name if it comes from a request.
  • Keep the session id private: with resources on other hosts, add <pd4tl:property name="pd4ml.sessionid.append" value="false"/>.
  • Check the HTML: enable="false" shows the HTML the tag would convert, as the browser sees it.
  • Ship pd4fonts.properties with font directories used by <pd4tl:usettf>.
  • Large documents: the HTML and the result are held in memory; forward mode also keeps the request in the session. Prefer direct mode, and paginate very large reports.

7. Troubleshooting

Symptom Cause and remedy
"The absolute uri ... cannot be resolved" The jar is not in WEB-INF/lib, the URI is misspelled (https:// instead of http://), or the container skips the jar when scanning for tag libraries (Tomcat: jarsToSkip in catalina.properties - add the PD4ML jar to tldScan/jarsToScan).
ClassNotFoundException / NoClassDefFoundError for javax.servlet... or jakarta.servlet... The URI does not match the container: 4.2-jk for Jakarta (Tomcat 10+), 4.2 for javax (Tomcat 9 and earlier).
"No tag [page.break] defined in tag library imported with prefix [pd4ml]" The tag library uses the prefix pd4ml: change it to pd4tl.
IllegalStateException The response was committed before the tag (flushed, or more than the page buffer written), or the page writes through getWriter(): move the tag to the top of the page, or use forward mode.
HTTP 500 "Invalid page format", "Invalid screen width", "Invalid page insets", "Invalid multicolumn definition" An attribute value in the wrong form; see 4.1.
HTTP 500 "can not create: [...]" The savefile directory does not exist or is not writable.
Images or styles missing Relative references resolve against the JSP's file; server-absolute paths need absolute URLs or <pd4tl:fix_base_path>; resources behind a login need the session id (default) or basicAuthentication. debug="true" logs what is loaded.
The PDF says "invalid url: ..." url is not an absolute http(s):// URL or a /-path.
The browser shows "Hello no cached PDF generation data found in the session context" Forward mode lost the session between the redirect and the servlet (cookies off and no URL rewriting, or a different host name).
Output is watermarked "PD4ML DEMO MODE" No license: put pd4ml.lic into WEB-INF/classes.
Fonts are replaced, or squares instead of characters The fonts are not available to PD4ML: provide them with <pd4tl:usettf> and use their family names in the CSS.
Downloaded file has the wrong extension Set fileName (and savefile's name) to match outputFormat.

8. Older tag library versions

Pages written for earlier versions keep working: the older URIs use the same tags.

URI Servlet API Version
http://pd4ml.com/tlds/4.2-jk Jakarta 1.2 (current)
http://pd4ml.com/tlds/4.2 javax 1.2 (current)
http://pd4ml.com/tlds/4.1-jk Jakarta 1.0
http://pd4ml.com/tlds/4.1, http://pd4ml.com/tlds/4.0 javax 1.0

Version 1.0 lacks the metadata and property tags, the transform attributes documentTitle, initialPageNumber, kerning, ligatures, protectPhysicalUnitDimensions, formFont, proxy and basicAuthentication, the permissions attribute encryption (and permission names in rights), and the usettf attribute allGlyphs. To use them, change the URI to 4.2 or 4.2-jk; nothing else in the page needs to change.

Pages written for PD4ML v3 (http://pd4ml.com/tlds/3.0, prefix pd4ml) need the new URI and the pd4tl prefix; the tags and their attributes are the same. See the v3 to v4 migration guide.

9. See also