如何在 C# 中使用 iText7 从页面顶部开始绘制长 table?
How to get a long table drawed starting from the top of page using iText7 in C#?
PDF Page Example
PDF是由几个添加到文档中的段落组成的,有一个很长的table需要设置从一页的顶部开始(而不是第一页),这意味着一个段落将被拆分分为两部分。
如果table很短(长度不超过页面高度),可以通过处理开始页面的页面事件来优雅地完成,只需添加table到固定位置和大小的canvas,并设置文档的上边距。
PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
PdfDocument pdfDoc = docEvent.GetDocument();
PdfPage page = docEvent.GetPage();
var currentPageNumber = pdfDoc.GetPageNumber(page);
var addingTablesOfPage = addingTables.Where(t => t.PageNumber == currentPageNumber && t.Prepared).ToList();
if (addingTablesOfPage != null && addingTablesOfPage.Count > 0)
{
PdfCanvas canvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);
PageSize pageSize = pdfDoc.GetDefaultPageSize();
var pageHeight = pageSize.GetHeight() - Constants.DocumentMargins[0] - Constants.DocumentMargins[2];
var pageWidth = pageSize.GetWidth() - Constants.DocumentMargins[1] - Constants.DocumentMargins[3];
var totalHeight = 0f;
var alignRight = false;
// add tables to the top of page
foreach (var table in addingTablesOfPage.OrderBy(t => t.Type).ToList())
{
var tableWidth = 0f;
float coordX = 0;
tableWidth = pageWidth;
coordX = pageSize.GetX() + doc.GetLeftMargin();
totalHeight += table.TableHeight;
float coordY = pageSize.GetTop() - Constants.DocumentMargins[0] - totalHeight;
Rectangle rect = new Rectangle(coordX, coordY, tableWidth, table.TableHeight);
var tableCanvas = new Canvas(canvas, rect);
tableCanvas.Add(table.Table);
tableCanvas.Close();
}
float topMargin = Constants.DocumentMargins[0] + totalHeight;
doc.SetTopMargin(topMargin);
}
else
{
doc.SetTopMargin(Constants.DocumentMargins[0]);
}
但是这里的table太长了,会被分成多页。据我所知,Canvas class 主要针对需要将元素添加到页面/XObject 上的特定预定义区域的情况,而不是将内容溢出到下一个区域。那么我怎样才能实现这种行为呢?
谢谢!
对您要实现的目标的描述非常模糊(不清楚它是围绕 table 的段落,还是段落之间的 table;不清楚 table 是否绑定到段落中的任何文本等),但我的回答应该会引导您走向正确的方向。
我将向您展示如何将适合当前页末尾的段落部分添加到文档中,然后添加跨越多个页面的 table,然后添加剩余部分你的段落。
首先,我们的段落需要一个自定义渲染器,它将只渲染当前适合页面的部分,并记住其余部分以便稍后添加。
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer leftover;
private CustomParagraphRenderer toDraw;
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (result.getStatus() == LayoutResult.PARTIAL) {
// Expected result here is that paragraph splits across pages
leftover = (CustomParagraphRenderer) result.getOverflowRenderer();
toDraw = (CustomParagraphRenderer) result.getSplitRenderer();
return new LayoutResult(LayoutResult.FULL, result.getSplitRenderer().getOccupiedArea(), null, null);
}
return result;
}
@Override
public void draw(DrawContext drawContext) {
if (toDraw != null) {
toDraw.draw(drawContext);
} else {
super.draw(drawContext);
}
}
@Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
}
现在,我们在文档中添加了一些占位符矩形,只是为了占用一些 space 用于测试目的,然后我们使用自定义渲染器添加我们的段落(请注意,预期段落将是尽可能在当前页面上呈现,剩下的部分会被记住),然后我们添加我们的 table 跨越多个页面,最后,我们添加我们的剩余段落部分。
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(outFileName));
Document document = new Document(pdfDocument);
document.setFontSize(20);
Div emptyArea = new Div().setHeight(400).setBackgroundColor(ColorConstants.RED);
document.add(emptyArea);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500; i++) {
sb.append(i).append(" ");
}
Paragraph p = new Paragraph(sb.toString());
p.setNextRenderer(new CustomParagraphRenderer(p).setParent(document.getRenderer()));
CustomParagraphRenderer paragraphRenderer = (CustomParagraphRenderer) p.createRendererSubTree();
// Add first part of the paragraph
document.getRenderer().addChild(paragraphRenderer);
Table table = new Table(3);
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 3; j++) {
table.addCell(new Cell().add(new Paragraph("Cell (" + i + ", " + j + ")")));
}
}
// Add big table
document.add(table);
// Add leftover paragraph part
document.getRenderer().addChild(paragraphRenderer.leftover.setParent(document.getRenderer()));
document.close();
结果如下。请注意,我特意添加了后续数字作为段落内容,以便很明显这是一个连续的段落,被 table:
打断
PDF Page Example
PDF是由几个添加到文档中的段落组成的,有一个很长的table需要设置从一页的顶部开始(而不是第一页),这意味着一个段落将被拆分分为两部分。
如果table很短(长度不超过页面高度),可以通过处理开始页面的页面事件来优雅地完成,只需添加table到固定位置和大小的canvas,并设置文档的上边距。
PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
PdfDocument pdfDoc = docEvent.GetDocument();
PdfPage page = docEvent.GetPage();
var currentPageNumber = pdfDoc.GetPageNumber(page);
var addingTablesOfPage = addingTables.Where(t => t.PageNumber == currentPageNumber && t.Prepared).ToList();
if (addingTablesOfPage != null && addingTablesOfPage.Count > 0)
{
PdfCanvas canvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);
PageSize pageSize = pdfDoc.GetDefaultPageSize();
var pageHeight = pageSize.GetHeight() - Constants.DocumentMargins[0] - Constants.DocumentMargins[2];
var pageWidth = pageSize.GetWidth() - Constants.DocumentMargins[1] - Constants.DocumentMargins[3];
var totalHeight = 0f;
var alignRight = false;
// add tables to the top of page
foreach (var table in addingTablesOfPage.OrderBy(t => t.Type).ToList())
{
var tableWidth = 0f;
float coordX = 0;
tableWidth = pageWidth;
coordX = pageSize.GetX() + doc.GetLeftMargin();
totalHeight += table.TableHeight;
float coordY = pageSize.GetTop() - Constants.DocumentMargins[0] - totalHeight;
Rectangle rect = new Rectangle(coordX, coordY, tableWidth, table.TableHeight);
var tableCanvas = new Canvas(canvas, rect);
tableCanvas.Add(table.Table);
tableCanvas.Close();
}
float topMargin = Constants.DocumentMargins[0] + totalHeight;
doc.SetTopMargin(topMargin);
}
else
{
doc.SetTopMargin(Constants.DocumentMargins[0]);
}
但是这里的table太长了,会被分成多页。据我所知,Canvas class 主要针对需要将元素添加到页面/XObject 上的特定预定义区域的情况,而不是将内容溢出到下一个区域。那么我怎样才能实现这种行为呢?
谢谢!
对您要实现的目标的描述非常模糊(不清楚它是围绕 table 的段落,还是段落之间的 table;不清楚 table 是否绑定到段落中的任何文本等),但我的回答应该会引导您走向正确的方向。
我将向您展示如何将适合当前页末尾的段落部分添加到文档中,然后添加跨越多个页面的 table,然后添加剩余部分你的段落。
首先,我们的段落需要一个自定义渲染器,它将只渲染当前适合页面的部分,并记住其余部分以便稍后添加。
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer leftover;
private CustomParagraphRenderer toDraw;
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (result.getStatus() == LayoutResult.PARTIAL) {
// Expected result here is that paragraph splits across pages
leftover = (CustomParagraphRenderer) result.getOverflowRenderer();
toDraw = (CustomParagraphRenderer) result.getSplitRenderer();
return new LayoutResult(LayoutResult.FULL, result.getSplitRenderer().getOccupiedArea(), null, null);
}
return result;
}
@Override
public void draw(DrawContext drawContext) {
if (toDraw != null) {
toDraw.draw(drawContext);
} else {
super.draw(drawContext);
}
}
@Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
}
现在,我们在文档中添加了一些占位符矩形,只是为了占用一些 space 用于测试目的,然后我们使用自定义渲染器添加我们的段落(请注意,预期段落将是尽可能在当前页面上呈现,剩下的部分会被记住),然后我们添加我们的 table 跨越多个页面,最后,我们添加我们的剩余段落部分。
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(outFileName));
Document document = new Document(pdfDocument);
document.setFontSize(20);
Div emptyArea = new Div().setHeight(400).setBackgroundColor(ColorConstants.RED);
document.add(emptyArea);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500; i++) {
sb.append(i).append(" ");
}
Paragraph p = new Paragraph(sb.toString());
p.setNextRenderer(new CustomParagraphRenderer(p).setParent(document.getRenderer()));
CustomParagraphRenderer paragraphRenderer = (CustomParagraphRenderer) p.createRendererSubTree();
// Add first part of the paragraph
document.getRenderer().addChild(paragraphRenderer);
Table table = new Table(3);
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 3; j++) {
table.addCell(new Cell().add(new Paragraph("Cell (" + i + ", " + j + ")")));
}
}
// Add big table
document.add(table);
// Add leftover paragraph part
document.getRenderer().addChild(paragraphRenderer.leftover.setParent(document.getRenderer()));
document.close();
结果如下。请注意,我特意添加了后续数字作为段落内容,以便很明显这是一个连续的段落,被 table:
打断