I'm using iTextPDF + FreeMarker for my project. Basically I load and fill an HTML template with FreeMarker and then render it to pdf with iTextPDF's XMLWorker
.
The template is:
<html>
<body style="font-family; ${fontName}">
<table>
<tr>
<td style="text-align: right">${timestampLabel} </td>
<td><b>${timestampValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${errorIdLabel} </td>
<td><b>${errorIdValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${systemIdLabel} </td>
<td><b>${systemIdValue}</b></td>
</tr>
<tr>
<td style="text-align: right">${descriptionLabel} </td>
<td><b>${descriptionValue}</b></td>
</tr>
</table>
</body>
</html>
And this is my code:
SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String errorId = "ERROR-01";
String systemId = "SYSTEM-01";
String description = "A SHORT DESCRIPTION OF THE ISSUE";
Map<String, String> parametersMap = new HashMap<String, String>();
parametersMap.put("fontName", fontName); //valid font name
parametersMap.put("timestampLabel", " TIMESTAMP:");
parametersMap.put("errorIdLabel", " ERROR ID:");
parametersMap.put("systemIdLabel", " SYSTEM ID:");
parametersMap.put("descriptionLabel", " DESCRIPTION:");
parametersMap.put("timestampValue", DATE_FORMAT.format(new Date()));
parametersMap.put("errorIdValue", errorId);
parametersMap.put("systemIdValue", systemId);
parametersMap.put("descriptionValue", description);
FreeMarkerRenderer renderer = new FreeMarkerRenderer(); //A utility class
renderer.loadTemplate(errorTemplateFile); //the file exists
String rendered = renderer.render(parametersMap);
File temp = File.createTempFile("document", ".pdf");
file = new FileOutputStream(temp);
Document document = new Document();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
document.setPageSize(new Rectangle(290f, 150f));
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
document.setMargins(10, 10, 10, 10);
PdfWriter writer = PdfWriter.getInstance(document, file);
document.open();
InputStream is = new ByteArrayInputStream(rendered.getBytes());
XMLWorkerFontProvider provider = new XMLWorkerFontProvider();
provider.register(fontFile); //the file exists
FontFactory.setFontImp(provider);
byte[] errorStyle = getErrorStyleByteArray(); //returns a byte array from a css file (works)
XMLWorkerHelper helper = XMLWorkerHelper.getInstance();
helper.parseXHtml(writer, document, is, new ByteArrayInputStream(errorStyle), provider);
document.close();
file.close();
This code works fine, but With the fixed height is a problem.
I.E. Let's say that:
errorId = "ERROR-01"
systemId = "SYSTEM-01"
description = "A SHORT DESCRIPTION OF THE ISSUE"
The produced document is:
If instead I use
errorId = "ERROR-01"
systemId = "SYSTEM-01"
description = "A SHORT DESCRIPTION OF THE ISSUE. THIS IS MULTILINE AND IT SHOULD STAY ALL IN THE SAME PDF PAGE."
The produced document is:
As you can see, in the last document I have two pages. I would like to have only one page which changes its height according to the content height.
Is something like this possible with iText?
You can not change the page size after you have added content to that page. One way to work around this, would be to create the document in two passes: first create a document to add the content, then manipulate the document to change the page size. That would have been my first reply if I had time to answer immediately.
Now that I've taken more time to think about it, I've found a better solution that doesn't require two passes. Take a look at HtmlAdjustPageSize
In this example, I first parse the content to a list of
Element
objects using this method:Note: I've been copy/pasting this method so many times that I decided to make it a static method in the
XMLWorkerHelper
class. It will be available in the next iText release.Important: I have done what I promised, this method is now available in the XML Worker release.
For testing purposes, I used static
String
values for HTML and CSS:You can see that I took HTML that looks more or less like the HTML you are dealing with.
I parse this HTML and CSS to an
ElementList
:Or, starting with XML Worker 5.5.4:
So far, so good. I haven't told you anything that you didn't already know, except this: I am now going to use this
el
twice:ColumnText
in simulation mode. ThisColumnText
isn't tied to any document or writer yet. The sole purpose to do this, is to know how much space I need vertically.ColumnText
for real. ThisColumnText
will fit exactly on a page of a size that I define using the results obtained in simulation mode.Some code will clarify what I mean:
The above code is useful for only one things: getting the
y
value that will be used to define the page size of theDocument
and the column dimension of theColumnText
that will be added for real:Please download the full HtmlAdjustPageSize.java code and change the value of
HTML
. You'll see that this leads to different page sizes.