当一行太大而无法放在第一页时,如何在 C# 的 Itext7 中禁用行拆分(不是表格!)?
How can I disable the splitting of rows (not tables!) in Itext7 for C# when one row is to big to fit on the first page?
如果 Itext7-Table 行是页面上的最后一行并且不适合第一页,我如何禁用行拆分?
我唯一找到的是 Itext5 的示例,其中方法 setSplitLate()
设置为 false
:
http://what-when-how.com/itext-5/dealing-with-large-tables-itext-5/(清单 4.19 HeaderFooter2.java)
有人对此有解决方案吗?
一般来说,建议您实现自己的自定义 TableRenderer 并自行处理布局。不过由于TableRenderer的layout algo确实非常难,建议大家改用下一个CellRenderer:
class CustomCellRenderer extends CellRenderer {
public CustomCellRenderer(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (LayoutResult.FULL != result.getStatus()) {
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new CustomCellRenderer((Cell)getModelElement());
}
}
正如您在此处看到的,如果单元格不适合页面,CustomCellRenderer 确保布局结果为 NOTHING(当前区域不能放置任何内容)而不是 PARTIAL(单元格可以拆分)。
在您的问题中,您提到了 HeaderFooter2 示例。这是移植到 iText7 的相同示例:https://github.com/itext/i7js-book/blob/develop/src/test/java/com/itextpdf/samples/book/part1/chapter04/Listing_04_19_HeaderFooter2.java
这就是您可以使用 CustomCellRendrer 更新它的方式:
for (Screening screening : screenings) {
movie = screening.getMovie();
cell = new Cell().add(new Paragraph(screening.getLocation()));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.format("%1$tH:%1$tM", screening.getTime())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.format("%d '", movie.getDuration())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(movie.getMovieTitle()));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.valueOf(movie.getYear())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new CustomCellRenderer(cell));
cell.add(PojoToElementFactory.getDirectorList(movie));
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new CustomCellRenderer(cell));
cell.add(PojoToElementFactory.getCountryList(movie));
table.addCell(cell);
}
如您所见,我通过 setNextRenderer
方法在单元格上设置了渲染器。 (请注意,只有 "body" 个单元格应使用您的自定义渲染器进行处理,因为我们假设页眉和页脚不会拆分)。
现在让我们看看结果。
之前split是这样处理的:
这就是现在的处理方式:
我在答案中使用的代码在 Java 中,但由于 iText 的 api 在 C# 中与在 Java 中相同,因此对您来说应该没有问题移植它。
这是我基于 Uladzimir 的解决方案。
基本上,您必须在单元格的基础上保存一个状态,允许仅在第一次对该特定单元格调用布局方法时使单元格上的 SplitRenderer 无效。
@Test
public void tableWithCellNotSplitted() {
File fOut = getFileOutput("table-cellsNotSplitter.pdf");
try {
final float MARGIN = 80;
Document document = getDocumentForOutput(fOut, true);
document.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
final String text = "Enjoyed minutes related as at on on. Is fanny dried as often me. Goodness as reserved raptures to mistaken steepest oh screened he.";
final String bigText = text + "\n" + text + "\n" + text + "\n" + text + "\n" + text + "\n" + text;
Table t = new Table(2);
t.setWidth(new UnitValue(UnitValue.PERCENT, 60));
for (int nRow = 0; nRow < 10; nRow++) {
for (int iCol = 0; iCol < 2; iCol++) {
String content = nRow == 1 ? bigText : text;
t.addCell(createNonSplittingCell2(content));
}
}
document.add(t);
document.close();
openOutputWithReader(fOut);
} catch (Exception e) {
Assert.fail(e.toString());
}
}
private Cell createPlainCell(String text) {
Cell c = new Cell().add(new Paragraph(text));
return c;
}
private Cell createNonSplittingCell1(String text) {
Cell c = new Cell().add(new Paragraph(text));
c.setNextRenderer(new NonSplittingRenderer1(c));
return c;
}
private Cell createNonSplittingCell2(String text) {
Cell c = new NotSplittingCell().add(new Paragraph(text));
c.setNextRenderer(new NonSplittingRenderer2(c));
return c;
}
public class NonSplittingRenderer1 extends CellRenderer {
public NonSplittingRenderer1(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (LayoutResult.FULL != result.getStatus()) {
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new NonSplittingRenderer1((Cell) getModelElement());
}
}
public class NonSplittingRenderer2 extends CellRenderer {
public NonSplittingRenderer2(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
Cell c = (Cell) modelElement;
if (c instanceof NotSplittingCell) {
if (LayoutResult.FULL != result.getStatus()) {
NotSplittingCell nsc = (NotSplittingCell) c;
if ( !nsc.isSplit()) {
nsc.setSplit(true);
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
}
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new NonSplittingRenderer2((Cell) getModelElement());
}
}
public class NotSplittingCell extends Cell {
private boolean _split;
public boolean isSplit() {
return _split;
}
public void setSplit(boolean split) {
_split = split;
}
}
此代码提供了三种创建单元格的方法:
- 没有渲染器的普通单元格(createPlainCell 方法)
- 像以前的解决方案一样带有渲染器的普通单元格(createNonSplittingCell1 方法)
- 具有修改版本渲染器的自定义单元格(createNonSplittingCell2 方法)
方案一明显分裂细胞
解决方案 2 在第一行之后停止写入单元格:每次他尝试渲染第二行的单元格时,布局方法都会使 splitrender 无效:只是 table 消失了。
解决方案 3 使用 Cell
、NotSplittingCell
的子类,它只存储单元格的拆分状态:仅当单元格从未拆分时,splitrenderer 才会失效。这样可以正确地保持单元格不分裂,但前提是可能
如果 Itext7-Table 行是页面上的最后一行并且不适合第一页,我如何禁用行拆分?
我唯一找到的是 Itext5 的示例,其中方法 setSplitLate()
设置为 false
:
http://what-when-how.com/itext-5/dealing-with-large-tables-itext-5/(清单 4.19 HeaderFooter2.java)
有人对此有解决方案吗?
一般来说,建议您实现自己的自定义 TableRenderer 并自行处理布局。不过由于TableRenderer的layout algo确实非常难,建议大家改用下一个CellRenderer:
class CustomCellRenderer extends CellRenderer {
public CustomCellRenderer(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (LayoutResult.FULL != result.getStatus()) {
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new CustomCellRenderer((Cell)getModelElement());
}
}
正如您在此处看到的,如果单元格不适合页面,CustomCellRenderer 确保布局结果为 NOTHING(当前区域不能放置任何内容)而不是 PARTIAL(单元格可以拆分)。
在您的问题中,您提到了 HeaderFooter2 示例。这是移植到 iText7 的相同示例:https://github.com/itext/i7js-book/blob/develop/src/test/java/com/itextpdf/samples/book/part1/chapter04/Listing_04_19_HeaderFooter2.java
这就是您可以使用 CustomCellRendrer 更新它的方式:
for (Screening screening : screenings) {
movie = screening.getMovie();
cell = new Cell().add(new Paragraph(screening.getLocation()));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.format("%1$tH:%1$tM", screening.getTime())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.format("%d '", movie.getDuration())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(movie.getMovieTitle()));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell().add(new Paragraph(String.valueOf(movie.getYear())));
cell.setNextRenderer(new CustomCellRenderer(cell));
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new CustomCellRenderer(cell));
cell.add(PojoToElementFactory.getDirectorList(movie));
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new CustomCellRenderer(cell));
cell.add(PojoToElementFactory.getCountryList(movie));
table.addCell(cell);
}
如您所见,我通过 setNextRenderer
方法在单元格上设置了渲染器。 (请注意,只有 "body" 个单元格应使用您的自定义渲染器进行处理,因为我们假设页眉和页脚不会拆分)。
现在让我们看看结果。
之前split是这样处理的:
这就是现在的处理方式:
我在答案中使用的代码在 Java 中,但由于 iText 的 api 在 C# 中与在 Java 中相同,因此对您来说应该没有问题移植它。
这是我基于 Uladzimir 的解决方案。
基本上,您必须在单元格的基础上保存一个状态,允许仅在第一次对该特定单元格调用布局方法时使单元格上的 SplitRenderer 无效。
@Test
public void tableWithCellNotSplitted() {
File fOut = getFileOutput("table-cellsNotSplitter.pdf");
try {
final float MARGIN = 80;
Document document = getDocumentForOutput(fOut, true);
document.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
final String text = "Enjoyed minutes related as at on on. Is fanny dried as often me. Goodness as reserved raptures to mistaken steepest oh screened he.";
final String bigText = text + "\n" + text + "\n" + text + "\n" + text + "\n" + text + "\n" + text;
Table t = new Table(2);
t.setWidth(new UnitValue(UnitValue.PERCENT, 60));
for (int nRow = 0; nRow < 10; nRow++) {
for (int iCol = 0; iCol < 2; iCol++) {
String content = nRow == 1 ? bigText : text;
t.addCell(createNonSplittingCell2(content));
}
}
document.add(t);
document.close();
openOutputWithReader(fOut);
} catch (Exception e) {
Assert.fail(e.toString());
}
}
private Cell createPlainCell(String text) {
Cell c = new Cell().add(new Paragraph(text));
return c;
}
private Cell createNonSplittingCell1(String text) {
Cell c = new Cell().add(new Paragraph(text));
c.setNextRenderer(new NonSplittingRenderer1(c));
return c;
}
private Cell createNonSplittingCell2(String text) {
Cell c = new NotSplittingCell().add(new Paragraph(text));
c.setNextRenderer(new NonSplittingRenderer2(c));
return c;
}
public class NonSplittingRenderer1 extends CellRenderer {
public NonSplittingRenderer1(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
if (LayoutResult.FULL != result.getStatus()) {
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new NonSplittingRenderer1((Cell) getModelElement());
}
}
public class NonSplittingRenderer2 extends CellRenderer {
public NonSplittingRenderer2(Cell modelElement) {
super(modelElement);
}
@Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
Cell c = (Cell) modelElement;
if (c instanceof NotSplittingCell) {
if (LayoutResult.FULL != result.getStatus()) {
NotSplittingCell nsc = (NotSplittingCell) c;
if ( !nsc.isSplit()) {
nsc.setSplit(true);
result.setStatus(LayoutResult.NOTHING);
result.setSplitRenderer(null);
result.setOverflowRenderer(this);
}
}
}
return result;
}
@Override
public IRenderer getNextRenderer() {
return new NonSplittingRenderer2((Cell) getModelElement());
}
}
public class NotSplittingCell extends Cell {
private boolean _split;
public boolean isSplit() {
return _split;
}
public void setSplit(boolean split) {
_split = split;
}
}
此代码提供了三种创建单元格的方法:
- 没有渲染器的普通单元格(createPlainCell 方法)
- 像以前的解决方案一样带有渲染器的普通单元格(createNonSplittingCell1 方法)
- 具有修改版本渲染器的自定义单元格(createNonSplittingCell2 方法)
方案一明显分裂细胞
解决方案 2 在第一行之后停止写入单元格:每次他尝试渲染第二行的单元格时,布局方法都会使 splitrender 无效:只是 table 消失了。
解决方案 3 使用 Cell
、NotSplittingCell
的子类,它只存储单元格的拆分状态:仅当单元格从未拆分时,splitrenderer 才会失效。这样可以正确地保持单元格不分裂,但前提是可能