【问题标题】:How to adjust the page height to the content height?如何将页面高度调整为内容高度?
【发布时间】:2015-11-17 18:42:47
【问题描述】:

我正在为我的项目使用 iTextPDF + FreeMarker。基本上,我使用 FreeMarker 加载并填充 HTML 模板,然后使用 iTextPDF 的XMLWorker 将其渲染为 pdf。

模板是:

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

这是我的代码:

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

这段代码可以正常工作,但是固定高度是个问题。

I.E.比方说:

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 可以实现这样的事情吗?

【问题讨论】:

    标签: java height itext itextpdf


    【解决方案1】:

    在向该页面添加内容后,您无法更改页面大小。解决此问题的一种方法是分两次创建文档:首先创建一个文档以添加内容,然后操作该文档以更改页面大小。如果我有时间立即回答,那将是我的第一个回复。

    现在我花了更多时间考虑它,我找到了一个更好的解决方案,不需要两次通过。看看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 两次:

    1. 我会在模拟模式下将列表添加到ColumnText。此ColumnText 尚未与任何文档或作者绑定。这样做的唯一目的是了解我需要多少垂直空间。
    2. 我会将列表添加到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 的值。您会看到这会导致不同的页面大小。

    【讨论】:

    • iText 的创建者正在回答我关于 iText 的问题。那好极了!!非常感谢,我回家试试这个
    • 非常有用的例子,很快就适应了段落文本,谢谢。是否有您创建 Bruno 的任何示例的索引?
    • 有没有办法实现相同的功能,但在单个字符串中包含 HTML 和 CSS?当我从 Server 获取完整的 HTML 文档时。
    • 我没有找到任何示例。你能分享一下吗?我有一个来自服务器的 html 文档,需要根据自定义字体的内容转换为具有高度的 PDF。
    • @SalmanNazir 你为什么不发布一个问题来展示你有什么?劫持 2014 年的问题并不是获得答案的方法。
    猜你喜欢
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 2021-10-01
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 1970-01-01
    相关资源
    最近更新 更多