c# - 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。我该怎么做?请帮我。谢谢。
解决方案
我一直在考虑你的问题已经有一段时间了,不幸的是,还没有找到任何合理的解决方案。
在 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是完全一样的。
推荐阅读
- angular - Angular 未使用 Dev Extreme 数据网格列和视图进行更新
- java - 多线程终止线程并允许访问其他线程
- css - 如何在 Bootstrap Vue 中更改箭头颜色?
- sql - Postgtres:考虑小时/分钟计算浮动天数
- r - 语法 %||% 的含义是什么?
- c++ - 抑制 LeakSanitizer 输出
- android - 检测已卸载的应用程序:如何向许多设备发送 Fcm 请求以了解其中一个已从 Firebase 未注册?
- amazon-web-services - 在 Node 中查找与已知公共 IP 地址关联的 EC2 资源 ID
- python - 出现错误:AttributeError: 'Node' object has no attribute 'output_masks' at flatten layer 用于展平嵌入输出
- c# - EFCore 跟踪记录状态:IsNew/IsLoaded