我在我的项目中使用iTextPDF + FreeMarker。基本上我使用FreeMarker加载并填充HTML模板,然后使用iTextPDF XMLWorker
将其渲染为pdf。
模板是:
<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>
这是我的代码:
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();
此代码工作正常,但固定高度是一个问题。
即。我们这样说:
errorId = "ERROR-01"
systemId = "SYSTEM-01"
description = "A SHORT DESCRIPTION OF THE ISSUE"
制作的文件是:
如果我使用
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."
制作的文件是:
如您所见,在上一篇文档中,我有两页。我想只有一个页面根据内容高度改变其高度。
iText可以这样吗?
答案 0 :(得分:2)
将内容添加到该页面后,您无法更改页面大小。解决此问题的一种方法是在两个过程中创建文档:首先创建文档以添加内容,然后操纵文档以更改页面大小。如果我有时间立即回答,那将是我的第一个回复。
现在我花了更多的时间来思考它,我找到了一个更好的解决方案,不需要两次通过。看看HtmlAdjustPageSize
在此示例中,我首先使用此方法将内容解析为Element
个对象的列表:
public ElementList parseHtml(String html, String css) throws IOException {
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssFile cssFile = XMLWorkerHelper.getCSS(new ByteArrayInputStream(css.getBytes()));
cssResolver.addCss(cssFile);
// HTML
CssAppliers cssAppliers = new CssAppliersImpl(FontFactory.getFontImp());
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.autoBookmark(false);
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline end = new ElementHandlerPipeline(elements, null);
HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, end);
CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
// XML Worker
XMLWorker worker = new XMLWorker(cssPipeline, true);
XMLParser p = new XMLParser(worker);
p.parse(new ByteArrayInputStream(html.getBytes()));
return elements;
}
注意:我已经多次复制/粘贴此方法,因此我决定将其作为XMLWorkerHelper
类中的静态方法。它将在下一个iText版本中提供。
重要提示:我完成了我的承诺,此方法现已在XML Worker版本中提供。
出于测试目的,我对HTML和CSS使用了静态String
值:
public static final String HTML = "<table>" +
"<tr><td class=\"ra\">TIMESTAMP</td><td><b>2014-11-28 11:06:09</b></td></tr>" +
"<tr><td class=\"ra\">ERROR ID</td><td><b>ERROR-01</b></td></tr>" +
"<tr><td class=\"ra\">SYSTEM ID</td><td><b>SYSTEM-01</b></td></tr>" +
"<tr><td class=\"ra\">DESCRIPTION</td><td><b>TEST WITH A VERY, VERY LONG DESCRIPTION LINE THAT NEEDS MULTIPLE LINES</b></td></tr>" +
"</table>";
public static final String CSS = "table {width: 200pt; } .ra { text-align: right; }";
public static final String DEST = "results/xmlworker/html_page_size.pdf";
您可以看到我使用的HTML看起来或多或少与您正在处理的HTML一样。
我将此HTML和CSS解析为ElementList
:
ElementList el = parseHtml(HTML, CSS);
或者,从XML Worker 5.5.4开始:
ElementList el = XMLWorkerHelper.parseToElementList(HTML, CSS);
到目前为止,这么好。我没有告诉你任何你不知道的事情,除了这个:我现在
将两次使用此el
:
ColumnText
。此ColumnText
尚未与任何文档或作者绑定。这样做的唯一目的是知道我需要垂直的空间。ColumnText
中。这个ColumnText
将完全适合我使用在模拟模式中获得的结果定义的大小的页面。有些代码会澄清我的意思:
// I define a width of 200pt
float width = 200;
// I define the height as 10000pt (which is much more than I'll ever need)
float max = 10000;
// I create a column without a `writer` (strange, but it works)
ColumnText ct = new ColumnText(null);
ct.setSimpleColumn(new Rectangle(width, max));
for (Element e : el) {
ct.addElement(e);
}
// I add content in simulation mode
ct.go(true);
// Now I ask the column for its Y position
float y = ct.getYLine();
上述代码仅适用于以下一项内容:获取用于定义y
的页面大小的Document
值和将ColumnText
的列维度添加为真实的:
Rectangle pagesize = new Rectangle(width, max - y);
// step 1
Document document = new Document(pagesize, 0, 0, 0, 0);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
ct = new ColumnText(writer.getDirectContent());
ct.setSimpleColumn(pagesize);
for (Element e : el) {
ct.addElement(e);
}
ct.go();
// step 5
document.close();
请下载完整的HtmlAdjustPageSize.java代码并更改HTML
的值。您会看到这导致不同的页面大小。