首页 > 解决方案 > C# iText7 从表中删除某些标题

问题描述

我想从创建的表中删除某些标题。

Table table = new Table(2).SetWidth(iText.Layout.Properties.UnitValue.CreatePercentValue(100));
Table headerTable1 = new Table(2).SetWidth(iText.Layout.Properties.UnitValue.CreatePercentValue(100));
headerTable1.AddCell(new Cell().Add(new Paragraph("Test1")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));
headerTable1.AddCell(new Cell().Add(new Paragraph("Test2")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));

table.AddHeaderCell(new Cell(1,2).Add(headerTable1));

Table headerTable2 = new Table(2).SetWidth(iText.Layout.Properties.UnitValue.CreatePercentValue(100));
headerTable2.AddCell(new Cell().Add(new Paragraph("Test1")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));
headerTable2.AddCell(new Cell().Add(new Paragraph("Test2")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));

table.AddHeaderCell(new Cell(1,2).Add(headerTable2));

Table headerTable3 = new Table(2).SetWidth(iText.Layout.Properties.UnitValue.CreatePercentValue(100));
headerTable3.AddCell(new Cell().Add(new Paragraph("Test1")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));
headerTable3.AddCell(new Cell().Add(new Paragraph("Test2")).SetWidth(iText.Layout.properties.UnitValue.CreatePercentValue(50)));

table.AddHeaderCell(new Cell(1,2).Add(headerTable3));

{Add Multiple Cells to Table}

一旦表格的高度大于 PageSize.A4.GetHeight()(假设我们使用的是 A4 内容),它会自动生成几个页面并添加上面的标题。从第二页开始,我想删除 headerTable2。我该怎么做?请帮我。谢谢。

标签: c#itext7

解决方案


我一直在考虑你的问题已经有一段时间了,不幸的是,还没有找到任何合理的解决方案。

在 iText7 中,为了覆盖某些布局元素的行为,应该扩展其默认渲染器(对于表格,它将是TableRenderer),然后在要处理的元素上设置此自定义渲染器的实例。

可以使用元素获取表格的标题Table#getHeader()并在其上设置渲染,但是有两个不容易克服的问题:

1) 对于每一页,iText 都会重新创建渲染器,因此您不能假设一个渲染器实例会传播到溢出的表渲染器。要知道渲染器有没有被使用,当然可以引入一些静态字段,但是

2) 表头的边框在父表的layout方法中被隐式处理。而且因为表格有很多边缘情况,默认layout方法确实非常复杂,重写它是个难题。不仅如此,很多TableRenderer's 字段都具有私有或默认访问权限,因此会给您带来额外的困难。

这就是为什么我认为你必须正视这样一个事实,即不可能以你想要的方式处理表格标题。

但是只有标题呢?iText 为客户提供了该IEvent功能。可以实现 BEGIN_PAGE(或 END_PAGE)事件(在您的情况下,它将是标题图)并使文档监听它。一旦事件被捕获,将执行绘图。

让我们看看它是如何实现的。

处理事件(下例中的 TableHeaderEventHandler)非常简单:

    TableHeaderEventHandler handler = new TableHeaderEventHandler(doc);
    pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, handler);

这就是实现此事件的方式:

    public class TableHeaderEventHandler implements IEventHandler {
    protected Table table;
    protected float tableHeight;
    protected Document doc;

    public TableHeaderEventHandler(Document doc) {
        this.doc = doc;
        initTable(false);
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        PdfPage page = docEvent.getPage();
        PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
        Rectangle rect = new Rectangle(pdfDoc.getDefaultPageSize().getX() + doc.getLeftMargin(),
                pdfDoc.getDefaultPageSize().getTop() - doc.getTopMargin(), 523, getTableHeight());
        new Canvas(canvas, pdfDoc, rect)
                .add(table);

        if (1 == doc.getPdfDocument().getPageNumber(page)) {
            initTable(true);
        }
    }

    public float getTableHeight() {
        return tableHeight;
    }

    private void initTable(boolean removeTheSecondRow) {
        table = new Table(3);
        table.addCell("row 1, 1");
        table.addCell(new Cell(1, 2).add(new Paragraph("row 1, 2 3")));
        if (!removeTheSecondRow) {
            table.addCell(new Cell(1, 3).add(new Paragraph("row 2, 1 2 3")));
        }
        table.addCell("row 3, 1");
        table.addCell("row 3, 2");
        table.addCell("row 3, 3");
        TableRenderer renderer = (TableRenderer) table.createRendererSubTree();
        renderer.setParent(new Document(new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))).getRenderer());
        tableHeight = renderer.layout(new LayoutContext(new LayoutArea(0, PageSize.A4))).getOccupiedArea().getBBox().getHeight();
        doc.setMargins(120 + tableHeight, 36, 36, 36);
    }
}

在上面的代码片段中,一旦将标题放在第一页上,我就会重新初始化它。请注意文档边距的处理:您可能不希望页面内容干扰页眉,这需要特别注意:

        TableRenderer renderer = (TableRenderer) table.createRendererSubTree();
        renderer.setParent(new Document(new PdfDocument(new PdfWriter(new ByteArrayOutputStream()))).getRenderer());
        tableHeight = renderer.layout(new LayoutContext(new LayoutArea(0, PageSize.A4))).getOccupiedArea().getBBox().getHeight();
        doc.setMargins(120 + tableHeight, 36, 36, 36);

我一直在玩的示例可以在 iText 的网站或 github 上找到:https ://github.com/itext/i7js-examples/blob/develop/src/test/java/com/itextpdf/samples/sandbox/事件/TableHeader.java

但是,上述解决方案存在一些非常明显的缺陷:

1)一个人应该border-collapsing自己处理。

这是一个艰难的问题,一个通用的解决方案需要大量的思考。但是,要解决特定情况,只需以正确放置边界的方式计算空间即可。

2) 应该明智地更新文档的区域。

让我们看一下上面代码生成的pdf。您可能想知道为什么页眉与第二页上文档的其他元素之间存在间隙。还有更神奇的是,为什么第三页+页没有空档: 在此处输入图像描述

原因如下:在处理事件时,已经将下一页添加到文档中。如何克服它?正如我在一开始所说的,通常会覆盖渲染器(这里是DocumentRenderer)来处理它。有关更多信息,请查看此答案:IText 7 How To Add Div or Paragraph in Header without Overlapping Page Content?

希望这会有用!

PS虽然已经用Java给出了答案,但是用C#复现应该没有问题,因为API是完全一样的。


推荐阅读