IText使用XML Worker防止跨多个页面的行划分

时间:2016-02-09 04:28:08

标签: java itext xmlworker page-break-inside

我们正在使用带有XML Worker的iText 5.5.7,并且遇到了长表的问题,其中在页面末尾运行的行被分成两部分到下一页(见图)。

我们已尝试按照Prevent page break in text block with iText, XMLWorkeriText Cut between pages in PDF from HTML table中的建议使用page-break-inside:avoid;但不起作用。

我们已经尝试了

  • 将每行包裹在<tbody>中并应用分页符避免(无效)
  • 定位tr, td并应用分页符(无效)
  • 将每个td的内容包装在div中并应用分页符(一旦到达页面末尾,itext就停止处理行)

我们的印象是page-break-inside:avoid受到支持,但还没有看到对此的确认。是否有使用XML worker创建此效果的示例或最佳实践,或者是执行此级别操作所需的Java API?

欢呼声

目前正在分页的行:

Rows are spilling on to the next page (see red outline)

所需效果:包含太多数据的行包装到下一页 Desired outcome, rows break to next page if too long (see green outline)

1 个答案:

答案 0 :(得分:8)

.NET开发人员,但您应该能够轻松翻译以下C#代码。

任何时候默认的XML Worker实现都不能满足您的需求,您基本上只需要查看源代码。首先,查看XML Worker是否支持Tags class中所需的标记。对于支持<table>样式的page-break-inside:avoid,有一个很好的实现,但仅在<table>级别,而不是行<tr>级别。幸运的是,覆盖TableEnd()方法并不是那么多。

如果支持标记,则需要通过继承AbstractTagProcessor来推广自己的自定义标记处理器,但不会去那里寻找此答案。

无论如何,关于代码。我们可以使用自定义page-break-inside:avoid属性并充分利用两个方面的优势,而不是通过更改HTML样式的行为来消除默认实现:

public class TableProcessor : Table
{
    // custom HTML attribute to keep <tr> on same page if possible
    public const string NO_ROW_SPLIT = "no-row-split";
    public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent)
    {
        IList<IElement> result = base.End(ctx, tag, currentContent);
        var table = (PdfPTable)result[0];

        if (tag.Attributes.ContainsKey(NO_ROW_SPLIT))
        {
            // if not set,  table **may** be forwarded to next page
            table.KeepTogether = false;
            // next two properties keep <tr> together if possible
            table.SplitRows = true;
            table.SplitLate = true;
        }
        return new List<IElement>() { table };
    }
}

一种简单的方法来生成一些测试HTML

public string GetHtml()
{
    var html = new StringBuilder();
    var repeatCount = 15;
    for (int i = 0; i < repeatCount; ++i) { html.Append("<h1>h1</h1>"); }

    var text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vestibulum sollicitudin luctus. Curabitur at eros bibendum, porta risus a, luctus justo. Phasellus in libero vulputate, fermentum ante nec, mattis magna. Nunc viverra viverra sem, et pulvinar urna accumsan in. Quisque ultrices commodo mauris, et convallis magna. Duis consectetur nisi non ultrices dignissim. Aenean imperdiet consequat magna, ac ornare magna suscipit ac. Integer fermentum velit vitae porttitor vestibulum. Morbi iaculis sed massa nec ultricies. Aliquam efficitur finibus dolor, et vulputate turpis pretium vitae. In lobortis lacus diam, ut varius tellus varius sed. Integer pulvinar, massa quis feugiat pulvinar, tortor nisi bibendum libero, eu molestie est sapien quis odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit.";

    // default iTextSharp.tool.xml.html.table.Table (AbstractTagProcessor)
    // is at the <table>, **not <tr> level
    html.Append("<table style='page-break-inside:avoid;'>");
    html.AppendFormat(
        @"<tr><td style='border:1px solid #000;'>DEFAULT IMPLEMENTATION</td>
            <td style='border:1px solid #000;'>{0}</td></tr>",
        text
    );
    html.Append("</table>");

    // overriden implementation uses a custom HTML attribute to keep:
    // <tr> together - see TableProcessor
    html.AppendFormat("<table {0}>", TableProcessor.NO_ROW_SPLIT);
    for (int i = 0; i < repeatCount; ++i)
    {
        html.AppendFormat(
            @"<tr><td style='border:1px solid #000;'>{0}</td>
            <td style='border:1px solid #000;'>{1}</td></tr>",
            i, text
        );
    }
    html.Append("</table>");
    return html.ToString();
}

最后解析代码:

using (var stream = new FileStream(OUTPUT_FILE, FileMode.Create))
{
    using (var document = new Document())
    {
        PdfWriter writer = PdfWriter.GetInstance(
            document, stream
        );
        document.Open();

        // instantiate custom tag processor and add to `HtmlPipelineContext`.
        var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
        tagProcessorFactory.AddProcessor(
            new TableProcessor(),
            new string[] { HTML.Tag.TABLE }
        );
        var htmlPipelineContext = new HtmlPipelineContext(null);
        htmlPipelineContext.SetTagFactory(tagProcessorFactory);

        var pdfWriterPipeline = new PdfWriterPipeline(document, writer);
        var htmlPipeline = new HtmlPipeline(htmlPipelineContext, pdfWriterPipeline);

        var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);
        var cssResolverPipeline = new CssResolverPipeline(
            cssResolver, htmlPipeline
        );

        var worker = new XMLWorker(cssResolverPipeline, true);
        var parser = new XMLParser(worker);
        using (var stringReader = new StringReader(GetHtml()))
        {
            parser.Parse(stringReader);
        }
    }
}

Full source

维护默认实现 - 首先将<table>保留在一起,而不是分成两页:

enter image description here

自定义实现会在第二个<table>中将保持在一起:

enter image description here