Generating PDF from XHTML and CSS2 template with Flying Saucer 9.0.4

In this post, I will share a small utility for creating PDF files from a template file written in XHTML and CSS 2.1. The template file is set up with a stylesheet that handles margins, page breaks and running top content. This is a complete example showing how to use the Flying Saucer 9.0.4 framework (based on IText 2.1.7 which has an open source license).

Very recently I got the following requirement from my customer: “I want to be able to convert a web page into PDF and instantly be able to download it from my browser“. So the very first thing I did was to search the net for tools, frameworks, reviews and blogs on the subject. On the very best frameworks for creating PDF files in Java, the IText framework, has changed it licensing models and now costs money so this was not an option. After a while I found the Flying Saucer project and these guys have seriously created an amazing tool which I ended up using. While searching the net, I found several blogs warning about using the Flying Saucer framework, however, looking at their GitHub page you’ll notice very recent commits and even when comparing the framework with others you’ll notice quickly that no other framework is actually simpler to use.

Flying Saucer can create PDFs from plain HTML, but the best thing in my opinion is the CSS 2.1 support. They have even build in some paging support that exists only in CSS 3. This makes it possible for you to both tell your customer that you actually can convert their webpages into PDFs, but also get help from your designers to work on the layout. So lets see some code.

Lets take a look at the XHTML template containg the complete CSS paging stylesheet and some demo text.

<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <title></title>

<meta charset="utf-8" />

<style type="text/css">

@page {
 size: A4;
 margin: 2.5cm;

@top-center {
 content: element(header, last-except)
 }
 }

@media all {
 .page-break { display: none; }
 div.header { display: none }
 }

@media print {
 .page-break { display: block; page-break-before: always; }
 div.header {
 display: block;
 position: running(header);
 }
 }

body {
 font-family: #fontfamily;
 }

p {
 font-size: 10pt;
 }

.center {
 text-align: center;
 }

</style>

</head>
<body>

<div class="header center"><small>Demo top header</small></div>

<h1 style="text-align: center; padding-top: 6cm">
 PDF Demo</h1>
<h3 style="text-align: center;">
 #subtitle</h3>

<div class="page-break"></div>

<h2>#test_header</h2>
<h4>#test_subheader1</h4>
<p>#test_paragraph</p>
</body>
</html>

The template should be easy to understand. You can find detailed information about how the paging is set up in the CSS3 specification which is here.  The second thing to notice is that I have inserted some parameters into the template (ie. #test_header) and these will later be replaced by some demo text when creating the PDF (see the unit test in the next section). I would be recommend that you read the Flying Saucer’s user guide which describes everything there is to know. And now lets look at the Java code that turns this template into a PDF file. The code itself should be very easy to understand, however, you should notice that I add to custom fonts. The two fonts I have added are Unicode fonts as the standard font in Flying Saucer is Latin-1. The advantage of using a Unicode font is that you can easily insert symbols and special characters as HTML entities into the template and get these converted and rendered correctly in the final PDF-file. The Source Sans Pro font used here is an open source and free to use font from Adobe.

public byte[] toPdf(String templatepath, Map<String, String> params) throws DocumentException, IOException, URISyntaxException {

 InputStream resourceAsStream = this.getClass().getResourceAsStream(templatepath);
 String template = IOUtils.toString(resourceAsStream);

// collect the generated data in a buffer
 ByteArrayOutputStream os = new ByteArrayOutputStream();

if (template != null) {

// customs params to be replaced
 for (String param : params.keySet()) {
 String replacewith = params.get(param);
 template = template.replaceFirst(param, safestring(replacewith));
 }

Calendar today = Calendar.getInstance();
 SimpleDateFormat shortDate = new SimpleDateFormat("dd-MM-yyyy");

// extra utiliy tags that can be used in the template
 template = template.replaceAll("#shortdate", "" + shortDate.format(today.getTime()));
 template = template.replaceAll("#pagebreak", TemplateUtils.getPageBreak());

// choose which font to use
 template = template.replaceAll("#fontfamily", TemplateUtils.UNICODE_TRUETYPE_SOURCE_SANS_PRO);

// example unicode characters for rendering two 'ballot box' characters
 template = template.replaceAll("#checked", "&#9745;");
 template = template.replaceAll("#unchecked", "&#9744;");

// prepare the 'Flying Saucer' and IText frameworks
 ITextRenderer render = new ITextRenderer();

// we need a font resolver for loading our custom fonts
 ITextFontResolver resolver = render.getFontResolver();

// unicode truetype font supporting 655 characters
 resolver.addFont(
 this.getClass().getResource("/fonts/SourceSansPro-Regular.ttf").getPath(),
 BaseFont.IDENTITY_H,
 BaseFont.NOT_EMBEDDED
 );

// unicode truetype font supporting 5572 characters
 // this font supports almost all the special characters ranging
 // from U+25A0 to U+25FF ("Geometric Shapes")
 resolver.addFont(
 this.getClass().getResource("/fonts/DejaVuSans.ttf").getPath(),
 BaseFont.IDENTITY_H,
 BaseFont.NOT_EMBEDDED
 );

// insert the template with replaced values
 render.setDocumentFromString(template);

// render XHTML and CSS 2.1
 render.layout();

// create the final PDF
 render.createPDF(os);
 }

// return the generated bytes
 return os.toByteArray();
 }

To test this code, simply run this JUnit-test.

@Test
 public void testToPdf() throws Exception {
 FlyingSaucerDemo demo = new FlyingSaucerDemo();

// insert some text into the template
 HashMap<String, String> params = new HashMap<String, String>();
 params.put("#toptext", "This running header is hidden in first page");
 params.put("#subtitle", "Demonstration of Flying Saucer Framework");
 params.put("#test_header", "This is a H2 header");
 params.put("#test_subheader", "This is a H4 header");
 params.put("#test_paragraph", "This is the first page");

// create the pdf
 byte[] bytes = demo.toPdf("/template.html", params);

// automatically load the file
 boolean view = true;
 if (view && Desktop.isDesktopSupported()) {
 try {
 String projectFolder = System.getProperty("user.dir");
 File file = new File(projectFolder + File.separator + "pdfdemo.pdf");
 FileOutputStream out = new FileOutputStream(file);
 out.write(bytes);
 Desktop.getDesktop().open(file);
 } catch (IOException ex) {

}
 }
 }

The resulting PDF should then look like this.

You can download or fork the full source code from my GitHub repository.

This entry was posted in Java, Web and tagged , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>