当前位置: 首页 > 知识库问答 >
问题:

如何将页面高度调整为内容高度?

秦新立
2023-03-14

我正在为我的项目使用 iTextPDF 免费标记。基本上,我使用免费标记加载并填充一个HTML模板,然后使用iTextPDF的XML工作程序将其呈现为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();

这段代码运行良好,但是固定高度是一个问题。

即假设:

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

共有1个答案

葛航
2023-03-14

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

现在我花了更多的时间来考虑这个问题,我找到了一个不需要两次通过的更好的解决方案。看看HtmlAdjust PageSize

在此示例中,我首先使用以下方法将内容解析为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版本中提供。

重要提示:我已经完成了我的promise,此方法现在可在XML Worker版本中使用。

出于测试目的,我对超文本标记语言和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。

我将这种超文本标记语言和CSS解析为ElementList

ElementList el = parseHtml(HTML, CSS);

或者,从 XML 工作线程 5.5.4 开始:

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

到目前为止,一切顺利。我没有告诉你任何你不知道的事情,除了这个:我现在要使用这个< code>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();

上面的代码只对一件事有用:获取< code>y值,该值将用于定义< code >文档的页面大小和将实际添加的< code>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代码并更改< code>HTML的值。你会发现这导致了不同的页面大小。

 类似资料:
  • 我正在使用ITextRenderer从HTML生成PDF,我需要做的是一张收银机收据。这张收据有动态宽度,当然还有动态内容。这就是说,内容的高度总是不同的,现在我正在努力寻找一种方法来根据内容调整PDF页面的高度。如果收据太大,最后会有一个长长的白色部分,如果是为了缩短PDF的页码,我只需要在一页中。 我正在使用设置页面大小,但根据宽度和数据计算内容高度几乎是不可能的(非常痛苦)。 这是生成 PD

  • 问题内容: 我在一个网格中嵌套了两个网格。不幸的是,右边的嵌套网格设置行的高度,以使左边和右边的网格都具有相同的高度,额外的空间在class的div之间共享。如何设置右侧嵌套网格的行以调整内容的大小,使其与左侧嵌套行的高度相同? 问题答案: 您可以尝试 参考 您也可以只使用或

  • 问题内容: 我想使一个组件占据Container的maximumAvailableHeight。例如,在下面粘贴的代码中,我将根框架定为800,600。我只想设置该框架的高度/宽度(并且我不想尝试对其子像素进行像素化)。如果运行此命令,则会看到UI对齐不良。 首先,我希望面板(位于根框架内)占据框架的100%的高度(在这种情况下,为800px减去用于绘制框架标题的空间)。 其次,在面板内部,我有一

  • 本文向大家介绍iframe如何自动调整高度?相关面试题,主要包含被问及iframe如何自动调整高度?时的应答技巧和注意事项,需要的朋友参考一下 未跨域时,在iframe中利用他的父窗口对象将本页面的滚动高度设置给iframe的height 跨域时,在iframe中将自己的的滚动高设置在本页面内的一个隐藏于父页面不跨域的iframe的hash值, 在隐藏的iframe中将值取出,同未跨域一样设置到要

  • 问题内容: 我希望TableView的高度适应填充的行数,以使其从不显示任何空行。换句话说,TableView的高度不应超过最后填充的行。我该怎么做呢? 问题答案: 如果您希望此操作有效,则必须设置。 然后,您可以将的高度与表格中包含的项目大小乘以固定单元格大小绑定在一起。 演示: 注意:我乘以fixedCellSize *(数据大小+ 1.01)以包含标题行。

  • 问题内容: 允许用户调整窗口的宽度和高度。是否存在允许用户仅调整高度大小的方法? 谢谢。 编辑: 下面的解决方案似乎不起作用。在360x600 JFrame上, 仍然允许完全拉伸JFrame的宽度,而设置则不允许任何拉伸。 问题答案: 下面的代码可以正确完成工作。