Using PD4ML custom tags with Struts or with any other J2EE UI frameworks, if JSP taglib integration is problematic

The specific of the Struts UI framework is that in some cases it takes control and opens output stream before PD4ML-enabled JSP page is loaded. On the other hand PD4ML needs an exclusive control over the output stream: it outputs binary data and any other output writers can corrupt it.

In order to solve the problem PD4ML offers the following solution.

(Since PD4ML 1.2.8 / Pro 2.1.4 there is an alternative to the approach below. The just generated PDF can be temporally stored to the hard drive and the current HTTP request can be forwarded to the new static PDF. See <pd4ml:savefile/> description for more details)

PD4ML transformer JSP pages need to be deployed to a separate web application outside of Struts context. It can be the same web application, but the PD4ML-enabled JSP pages should be out of control of the Struts dispatching servlet. (Can be achieved by web.xml settings).

If you want to separate PD4ML transforming and your application business logic – it is possible as well. Run a servlet runtime (like Tomcat) on a separate hardware unit and deploy PD4ML to it.

For our example the separated web application is associated with name separate_web_app.

Create a PD4ML transformer JSP page transformer.jsp in separate_web_app like the following.

<%@ taglib uri="/WEB-INF/tlds/pd4ml.tld" prefix="pd4ml" %><%@page
contentType="text/html; charset=ISO8859_1"%><pd4ml:transform
screenWidth="400"
pageFormat="A5"
pageOrientation="landscape"
pageInsets="15,15,15,15,points"
enableImageSplit="false"
inline="true"
fileName="myreport.pdf"
interpolateImages="false"

url="http://mainserver/struts/report.jsp">

<pd4ml:footer
titleTemplate="$[title]"
pageNumberTemplate="page $[page] of $[total]"
titleAlignment="left"
pageNumberAlignment="right"
fontSize="12"
areaHeight="14"/>

</pd4ml:transform>

 

Replace url attribute value of <pd4ml:transform> with the actual URL of your Struts (JSP) page, that should be converted to PDF.

The HTML content of transformer.jsp is ignored completely, but as usually: it should be no any white space character before <pd4ml:transform> tag.

At this point you can access the transformer.jsp and force PDF conversion of the specified source. But in most of the cases a session ID (JSESSIONID) propagation is important for proper Struts (or other JSP-based frameworks) functionality.

If the url attribute of <pd4ml:transform> is set, than PD4ML takes its value and appends to it JSESSIONID parameter. The value for the parameter it takes from HTTP request variable pd4session.

So a request for PDF generation from Struts context can look like that:

<%
response.sendRedirect( "/separate_web_app/transformer.jsp?pd4session=" +
session.getId() );
%>

New information: using of <pd4ml:savefile> tag allows us to save the just-generated PDF to the server’s local drive and to redirect the initial PDF generation HTTP request to the new static PDF file.

 

 

Using PD4ML custom tags with ColdFusion

The described integration method was tested with ColdFusion MX 6.1 enterprise and development editions running under Jrun4

Making PD4ML available in ColdFusion web application.

Copy pd4ml.jar, pd4ml_tl.jar and pd4ml.tld (pd4ml_demo.jar, pd4ml_tl_demo.jar and pd4ml.tld) to the directory WEB-INF/lib of your CF-enabled application. By default it is ${jrun4}/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/lib.Restart the CF runtime!

Creating a PD4ML-enabled .cfm page

<cfimport taglib="/WEB-INF/lib/pd4ml.tld" prefix="pd4ml"><pd4ml:transform
screenWidth="400"
pageFormat="A5"
pageOrientation="landscape"
pageInsets="15,15,15,15,points"
enableImageSplit="false"
inline="true"
fileName="myreport.pdf"
encoding="ISO8859_1"
interpolateImages="false">

<pd4ml:header
titleTemplate="$[title]"
pageNumberTemplate="page $[page]"
titleAlignment="left"
pageNumberAlignment="right"
color="##008000"
initialPageNumber="1"
pagesToSkip="1"
fontSize="14"
areaHeight="18"/>
<html>
<head>
<title>Hello, CF!</title>
<style>
td {
font-family: "Sans-Serif";
font-size: 12;
}
</style>
</head>
<body>

<img src="images/logos.gif" width="125" height="74">
<p>
<font face="tahoma">Hello, CF!</font>

<p>
<cfoutput>
Today's date is #DateFormat(Now(), "dd.mm.yyyy")#
</cfoutput>
<p>

<table border="0" width="300">

<cfloop index = "LoopCount" from = "1" to = "25">
<cfset x = variables.LoopCount mod 2 >

<tr>
<cfoutput>
<cfif variables.LoopCount IS "1">
<td bgcolor="##d0d0d0">Euros</td>
<td bgcolor="##d0d0d0">Dollars</td>
<td bgcolor="##d0d0d0">Pounds</td>
<cfelseif variables.x IS NOT "1">
<td>#LoopCount * 10# &##128;</td>
<td>#LoopCount * 10# $</td>
<td>#LoopCount / 10 * 2# &pound;</td>
<cfelse>
<td bgcolor="##e7e7e7">#LoopCount * 10# &##128;</td>
<td bgcolor="##e7e7e7">#LoopCount + 17# $</td>
<td bgcolor="##e7e7e7">#LoopCount / 10 * 2# &pound;</td>
</cfif>
</cfoutput>
</tr>

</cfloop>

</table>

</body>
</html>
</pd4ml:transform>

Comments:
1.There is no any white space character before <pd4ml:transform> tag. If there are any, than they appear in the resulting PDF file and corrupt it.
2.‘#’ characters have special meanings in ColdFusion. The character in the PD4ML header color definition is unescaped by duplication of ‘#’

Troubleshooting

Error: “The type for attribute authorName of tag transform could not be determined”
Solution: Jrun engine needs to be restarted. Newly installed PD4ML tag library is not known to the runtime.

Error: MS Internet Explorer 6.0 and Acrobat Reader 6.0.1 (or its newer version) show blank screen instead of resulting PDF. The problem is not existent with other browsers and Acrobat Reader’s versions.
Solution: Rename your .cfm page to .pdf and add the corresponding file extensions mapping to WEB-INF/web.xml. Access the page using the new name ended with .pdf (instead of .cfm).
The extension mapping code to be added:

<servlet-mapping>
<servlet-name>CfmServlet</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>

Error: <pd4ml:page.break/> causes an error.
Solution: It seems Jrun4 does not allow dots in custom JSP tag names. To fix the issue open WEB-INF/lib/pd4ml.tld in text editor, duplicate (copy-paste) page.break tag description section. In the copied section replace page.break tag name with page_break. And than replace <pd4ml:page.break/> tags in your .cfm code with <pd4ml:page_break/>

Temporary saving generated PDF to hard drive

With <pd4ml:savefile> tag you have possibility to store just generated PDF to hard drive and redirect user’s browser to read the PDF as static resource or to redirect the request to another URL for PDF post-processing.

Note: the tag should be nested to <pd4ml:transform> and have no body.

Usage 1.

<pd4ml:savefile
uri="/WEB/savefile/saved/"
dir="D:/spool/generated_pdfs"
redirect="pdf"
debug="false"/>

 

The tag above forces PD4ML to save the generated PDF to D:/spool/generated_pdfs with an autogenerated name.

It is expected, that local directory D:/spool/generated_pdfs corresponds to URL http://yourserver.com/WEB/savefile/saved/ (as given in “uri attribute)

After generation PD4ML will send to client’s browser a redirect command with URL like that:

http://yourserver.com/WEB/savefile/saved/generated_name.pdf

Usage 2.


<pd4ml:savefile
dir="D:/spool/generated_pdfs"
redirect="/mywebapp/send_pdf_by_email.jsp"
debug="false"/>

The tag above forces PD4ML to save the generated PDF to D:/spool/generated_pdfs with an autogenerated name.

After that it forwards to /mywebapp/send_pdf_by_email.jsp with a parameter filename=<pdfname>.

So send_pdf_by_email.jsp can read file name

String fileName = request.getParameter(“filename”);

build full path

String path = “D:/spool/generated_pdfs” + “/” + fileName;

read the just-generated PDF file and and perform postprocessing or other actions (like email sending).

In both cases above you can predefine PDF file name with “name” attribute. If a file with the name is already exists in D:/spool/generated_pdfs, than the new file name is appended with an autoincremented numeric value.

Defining PDF document footer (or header) with JSP custom tag

The <pd4ml:header> and <pd4ml:footer> JSP tags as well as inlinefileName and interpolateImages attributes of <pd4ml:transform> tag are available since v1.0.5


<%@ taglib uri="http://pd4ml.com/tlds/pd4ml/2.6" prefix="pd4ml" %><%@page
contentType="text/html; charset=ISO8859_1"%><pd4ml:transform
screenWidth="400"
pageFormat="A5"
pageOrientation="landscape"
pageInsets="15,15,15,15,points"
enableImageSplit="false"
inline="true"
fileName="footer.pdf"
interpolateImages="false">

<pd4ml:footer
1 titleTemplate="title: $[title]"
2 pageNumberTemplate="page $[page]"
titleAlignment="left"
pageNumberAlignment="right"
color="#008000"
3 initialPageNumber="1"
4 pagesToSkip="1"
fontSize="14"
5 areaHeight="18"/>

<html>
<head>
<title>pd4ml header/footer test</title>
<style type="text/css">
body {
color: #000000;
background-color: #FFFFFF;
font-family: Tahoma, "Sans-Serif";
font-size: 10pt;
}
</style>
</head>
<body>
<img src="images/logos.gif" width="125" height="74">
<p>
Hello, World!
<pd4ml:page.break/>
<table width="100%" style="background-color: #f4f4f4; color: #000000">
<tr>
<td>
Hello, New Page!
</td>
</tr>
</table>
</body>
</html>
</pd4ml:transform>

Comments:
1. Title template definition. A string that can optionally contain placeholders $[title] for a title value taken from HTML’s <title> tag, $[page] for a page counter value.
2. Page number template definition. A string with placeholder $[page] for a page counter value.
3. The attribute initializes internal page counter with the given value.
4. The attribute defines, that 1 page should not contain footer information.
5. Footer area height in points.

PD4ML also accepts syntax like ${var}, but it has special meaning in the most recent Java Servlet API versions. In order to avoid notation conflicts PD4ML additionally supports $[var] placeholders since v3.x.

Surrounding of HTML/JSP content with tags

Note: some combinations of MS Internet Explorer and Adobe Acrobat reader plugin versions are buggy. Instead of a PDF generation result MS IE displays a blank page. Check our online Support/HelpDesk for a possible workaround.


1 <%@ taglib uri="http://pd4ml.com/tlds/pd4ml/2.6" prefix="pd4ml" %><%@page
contentType="text/html; charset=ISO8859_1"%><pd4ml:transform
screenWidth="400"
pageFormat="A5"
pageOrientation="landscape"
pageInsets="100,100,100,100,points"
enableImageSplit="false">

<html>
<head>
<title>pd4ml test</title>
<style type="text/css">
body {
color: red;
background-color: #FFFFFF;
font-family: Tahoma, "Sans-Serif";
font-size: 10pt;
}
</style>
</head>
<body>
2 <img src="images/logos.gif" width="125" height="74">
<p>
Hello, World!
3 <pd4ml:page.break/>
<table width="100%" style="background-color: #f4f4f4; color: #000000">
<tr>
<td>
Hello, New Page!
</td>
</tr>
</table>
</body>
</html>
4 </pd4ml:transform>

 

Comments:
1. PD4ML JSP taglib declaration and opening transform tag. JSP content surrounded with <pd4ml:transform>and </pd4ml:transform> tags is passed to the PD4ML converter.
2. Image should be referenced with relative path. Absolute URLs, likesrc=”http://myserver:80/path/to/img.gif” are allowed as well, but src=”/path/to/img.gif” not.
3. The directive forces PD4ML converter to insert a page break to the output PDF.
4. Closing of the transformation tag. Any content that appears after the tag is ignored.
5.There is a CSS bug in JDKs older than v1.5b2. In order to avoid it, use CSS class names lowercased. (Irrelevant since PD4ML v3.x)