How to adjust the page height to the content height?

前端 未结 1 1008
[愿得一人]
[愿得一人] 2020-11-28 17:09

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

相关标签:
1条回答
  • 2020-11-28 17:24

    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.

    0 讨论(0)
提交回复
热议问题