How to adjust the page height to the content height?

為{幸葍}努か 提交于 2019-11-26 08:38:22

问题


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}&nbsp;</td>
                <td><b>${timestampValue}</b></td>
            </tr>
            <tr>
                <td style=\"text-align: right\">${errorIdLabel}&nbsp;</td>
                <td><b>${errorIdValue}</b></td>
            </tr>
            <tr>
                <td style=\"text-align: right\">${systemIdLabel}&nbsp;</td>
                <td><b>${systemIdValue}</b></td>
            </tr>
            <tr>
                <td style=\"text-align: right\">${descriptionLabel}&nbsp;</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:

\"enter

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:

\"enter

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?


回答1:


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:

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;
}

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:

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";

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:

ElementList el = parseHtml(HTML, CSS);

Or, starting with XML Worker 5.5.4:

ElementList el = XMLWorkerHelper.parseToElementList(HTML, CSS);

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:

  1. I'll add the list to a ColumnText in simulation mode. This ColumnText isn't tied to any document or writer yet. The sole purpose to do this, is to know how much space I need vertically.
  2. I'll add the list to a ColumnText for real. This ColumnText 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:

// 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();

The above code is useful for only one things: getting the y value that will be used to define the page size of the Document and the column dimension of the ColumnText that will be added for real:

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();

Please download the full HtmlAdjustPageSize.java code and change the value of HTML. You'll see that this leads to different page sizes.



来源:https://stackoverflow.com/questions/27186661/how-to-adjust-the-page-height-to-the-content-height

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!