简体   繁体   English

绘制表格的自定义边框,在itext7中具有更大的灵活性

[英]Draw custom borders for table with more flexibility in itext7

Previously I asked a question about drawing custom table borders: Draw custom borders for table spanning across more than one page in itext7 以前我问过一个有关绘制自定义表格边框的问题: 在itext7中为跨表格的表格绘制自定义边框

And there an answer was provided with a way to draw custom borders, but it does not allow to affect the complete row or column line (for example to tilt it slightly) because cell borders are drawn separately for each cell. 答案提供了一种绘制自定义边框的方法,但是它不允许影响完整的行或列线(例如,将其稍微倾斜),因为为每个单元格分别绘制了单元格边框。

I want to add some randomness to table borders like in the following screenshot: 我想在表格边框中添加一些随机性,如以下屏幕截图所示:

桌子边框

Here is the code I have that works for tables that fit into a single page, but if table is spanned across several pages the code does not work anymore: 这是我适用于适合单个页面的表的代码,但是如果表跨多个页面,则该代码将不再起作用:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
        Document doc = new Document(pdfDoc);
        doc.add(new Paragraph("Table event"));
        Table table = new Table(UnitValue.createPercentArray(3)).useAllAvailableWidth();
        table.setNextRenderer(new DottedLineTableRenderer(table, new Table.RowRange(0, 0)));
        String s ="";
        for(int i=0;i<35;i++){
        s+="\nTest";
         }
        table.addCell(new Cell().add(new Paragraph(s)).setBorder(Border.NO_BORDER));
        table.addCell(new Cell().add(new Paragraph("A2")).setBorder(Border.NO_BORDER));
        table.addCell(new Cell().add(new Paragraph("A3")).setBorder(Border.NO_BORDER));


        doc.add(table);


        doc.close();

private class DottedLineTableRenderer extends TableRenderer {
    public DottedLineTableRenderer(Table modelElement, Table.RowRange rowRange) {
        super(modelElement, rowRange);
    }

    @Override
    public void drawChildren(DrawContext drawContext) {
        super.drawChildren(drawContext);
        PdfCanvas canvas = drawContext.getCanvas();
        int maxLineTo = 5;
        int minLineTo = 2;
        int lineToHorizontalLine = (int)(Math.random() * maxLineTo) + minLineTo;

        int maxSkewHor = 5;
        int minSkewHor = 2;
        int skewHorizontalLine = (int)(Math.random() * maxSkewHor) + minSkewHor;

        int maxVerticalLine = 5;
        int minVerticalLine = 2;
        int lineToVerticalLine = (int)(Math.random() * maxVerticalLine) + minVerticalLine;


        canvas.setLineWidth(2).setStrokeColor(new DeviceRgb(222, 27, 27));

        // first horizontal line
        CellRenderer[] cellRenderers = rows.get(0);
        canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getLeft()-lineToHorizontalLine,
                cellRenderers[0].getOccupiedArea().getBBox().getTop());
        canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight()+lineToHorizontalLine,
                cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getTop());


        for (int i = 0; i < rows.size(); i++) {
            skewHorizontalLine = (int)(Math.random() * maxSkewHor) + minSkewHor;
            lineToHorizontalLine = (int)(Math.random() * maxLineTo) + minLineTo;
            cellRenderers = rows.get(i);
            // horizontal lines
            canvas.moveTo(cellRenderers[0].getOccupiedArea().getBBox().getX()-lineToHorizontalLine,
                    cellRenderers[0].getOccupiedArea().getBBox().getY()+skewHorizontalLine);
            canvas.lineTo(cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getRight()+lineToHorizontalLine,
                    cellRenderers[cellRenderers.length - 1].getOccupiedArea().getBBox().getBottom());

            // first vertical line
            Rectangle cellRect = cellRenderers[0].getOccupiedArea().getBBox();
            canvas.moveTo(cellRect.getLeft(), cellRect.getBottom());

            canvas.lineTo(cellRect.getLeft(), cellRect.getTop()+lineToVerticalLine );

            // vertical lines
            for (int j = 0; j < cellRenderers.length; j++) {
                lineToVerticalLine = (int)(Math.random() * maxVerticalLine) + minVerticalLine;


                cellRect = cellRenderers[j].getOccupiedArea().getBBox();
                canvas.moveTo(cellRect.getRight(), cellRect.getBottom()-lineToVerticalLine);
                canvas.lineTo(cellRect.getRight(), cellRect.getTop()+lineToVerticalLine); //ячейки

            }
        }
        canvas.stroke();
    }
}

I want to draw custom borders by myself :) 我想自己绘制自定义边框:)

First of all, we need to override the renderer correctly, ie override getNextRenderer() method. 首先,我们需要正确地覆盖渲染器,即,覆盖getNextRenderer()方法。 Currently TableRenderer is quite problematic to override because the parameterless constructor of TableRenderer is not accessible, and the other constructors do some implicit work that changes the state. 当前, TableRenderer重写存在很大问题,因为无法访问TableRenderer的无参数构造函数,而其他构造函数执行一些更改状态的隐式工作。 But we can still work around this problem with the following code: 但是我们仍然可以使用以下代码解决此问题:

@Override
public IRenderer getNextRenderer() {
    CustomTableRenderer nextTable = new CustomTableRenderer((Table) modelElement);
    nextTable.rows.clear();
    nextTable.rowRange = null;
    return nextTable;
}

Disclaimer : since the answer uses private implementation details of TableRenderer it may not work in the future. 免责声明 :由于答案使用TableRenderer私有实现细节,因此将来可能无法使用。 It works with 7.1.6 which is the latest released version at the moment of writing this post. 在撰写本文时,它与最新发布的版本7.1.6配合使用。 You should create a custom fork of the code for such purposes. 为此,您应该创建代码的自定义派生。 Pull requests are welcome as well. 也欢迎请求请求

If we look at the implementation of TableRenderer we see that the class contains heights ( line in code ) and countedColumnWidth ( line in code ) fields which sound interesting, but they are private . 如果我们看一下TableRenderer的实现,我们会看到该类包含heights代码中的行 )和countedColumnWidth代码中的行 )字段,这听起来很有趣,但它们是private It means that we should create our custom fork of iText (source code available at https://github.com/itext/itext7 ), make those fields protected and use them in our subclass. 这意味着我们应该创建iText的自定义派生(源代码可从https://github.com/itext/itext7获得 ), protected这些字段并在我们的子类中使用它们。

You may use reflection in your code instead at your own risk, but it should not be used because it may not work with your JVM (changing accessibility modifiers is strongly discouraged) or may not work in the next version of iText, or may not work for another reason. 你可以在你的代码,而不是您自己的风险使用反射,但它不应该被使用,因为它可能无法与您的JVM工作(改变访问修饰符是强烈反对),或者可能不会在iText的下一个版本的工作,或者可能无法正常工作由于另一个原因。 I will not add reflection code in my answer to further discourage its usage. 我不会在答案中添加反射代码以进一步阻止其使用。

All we need to do it override drawBorders() method. 我们需要做的就是覆盖drawBorders()方法。 Here is the code that already adds some randomness to the lines. 这是已经为行添加一些随机性的代码。

private static class CustomTableRenderer extends TableRenderer {
    public CustomTableRenderer(Table modelElement) {
        super(modelElement);
    }

    @Override
    public IRenderer getNextRenderer() {
        CustomTableRenderer nextTable = new CustomTableRenderer((Table) modelElement);
        nextTable.rows.clear();
        nextTable.rowRange = null;
        return nextTable;
    }

    @Override
    protected void drawBorders(DrawContext drawContext) {
        PdfCanvas canvas = drawContext.getCanvas();
        canvas.saveState();
        canvas.setStrokeColor(ColorConstants.RED);

        Random r = new Random();

        // Draw vertical lines
        float curX = getOccupiedAreaBBox().getLeft();
        for (int i = 0; i <= countedColumnWidth.length; i++) {
            canvas.moveTo(curX, getOccupiedAreaBBox().getTop() + 3);
            canvas.lineTo(curX + r.nextInt(4), getOccupiedAreaBBox().getBottom() - 3);
            if (i < countedColumnWidth.length) {
                float curWidth = countedColumnWidth[i];
                curX += curWidth;
            }
        }

        // Draw horizontal lines
        float curY = getOccupiedAreaBBox().getBottom();
        for (int i = 0; i <= heights.size(); i++) {
            canvas.moveTo(getOccupiedAreaBBox().getLeft() - 3, curY);
            canvas.lineTo(getOccupiedAreaBBox().getRight() + 3, curY + r.nextInt(4));
            if (i < heights.size()) {
                float curHeight = heights.get(i);
                curY += curHeight;
            }
        }

        canvas.stroke();
        canvas.restoreState();
    }
}

To activate the custom renderer, set it to the table just before adding to the document: 要激活自定义渲染器,请在添加到文档之前将其设置为表:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
Document doc = new Document(pdfDoc);

Table table = new Table(UnitValue.createPercentArray(new float[]{30, 30}));
for (int i = 0; i < 40; i++) {
    table.addCell(new Cell().add(new Paragraph("Hello")));
    table.addCell(new Cell().add(new Paragraph("World")));
    table.startNewRow();
}
table.setNextRenderer(new CustomTableRenderer(table));
doc.add(table);

This is how the resultant table looks like: 结果表如下所示:

结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM