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
- 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-Typefor the chosen output format,Content-disposition(inlineorattachment, with the file name), and cache headers. - 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. - 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
savefileandforward). - 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 |
|
protect |
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: includerequest.getContextPath(). Withforward, it is relative to the application. - The automatic name ends in
.pdfwhatever the output format: setnamefor other formats. nameis used as a path belowdir: 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_xhtmlturns 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_entitiesreplaces numeric character references (П,р) with the characters, which helped right-to-left scripts in older versions. It also turns escaped markup (<) 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
/inurl,forwardandsavefile'sredirectare relative to the server root: addrequest.getContextPath(). - Set
pageInsetsandfileNameexplicitly: the defaults (10/20/30/40 mm,file.pdf) rarely fit. pageFormatnames are case-sensitive (A4,LETTER,ArchA); numbers inpageInsets,watermarkBoundsandmulticolumnmust be whole numbers.- Create the
savefiledirectory in advance, and checknameif 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.propertieswith 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
- PD4ML proprietary CSS media types, properties and tags - the
<pd4ml:...>extension tags usable in the converted HTML
