IText 使用 XML Worker 防止跨多个页面的行中断

IText prevent row breaking across multiple pages using XML Worker

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

我们已尝试按照 and iText Cut between pages in PDF from HTML table 中的建议使用 page-break-inside:avoid;,但没有效果。

我们试过了

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

干杯

当前跨页拆分的行数:

预期效果:数据过多的行换行到下一页

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

任何时候默认的 XML Worker 实现不能满足您的需求,您基本上只能通过查看源代码来练习。首先看看XML Worker是否支持你想要的标签在Tags class. There's a nice implementation for <table> that supports the page-break-inside:avoid style, but it only works at the <table> level, not the row <tr> level. Luckily, it's not that much work to override the End() method for Table.

如果标签 不受 支持,您需要通过继承自 AbstractTagProcessor 来推出自己的自定义标签处理器,但不会去那里寻找这个答案。

不管怎样,继续写代码。我们可以使用自定义的 HTML 属性,而不是通过更改 page-break-inside:avoid 样式的行为来破坏默认实现,并获得两全其美的效果:

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> 保持在一起而不是分成两页:

并且自定义实现在第二个 <table>:

中将 保持在一起