绘制表格的自定义边框,在itext7中具有更大的灵活性 [英] Draw custom borders for table with more flexibility in itext7

查看:151
本文介绍了绘制表格的自定义边框,在itext7中具有更大的灵活性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

之前,我曾问过一个有关绘制自定义表格边框的问题:

Previously I asked a question about drawing custom table borders: Draw custom borders for table spanning across more than one page in 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 :)

推荐答案

首先,我们需要正确覆盖渲染器,即覆盖getNextRenderer()方法.当前TableRenderer的覆盖非常成问题,因为TableRenderer的无参数构造函数不可访问,并且其他构造函数执行一些隐式工作以更改状态.但是我们仍然可以使用以下代码解决此问题:

First of all, we need to override the renderer correctly, i.e. override getNextRenderer() method. 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. 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;
}

免责声明:由于答案使用了TableRenderer的私有实现详细信息,因此将来可能无法使用.它与7.1.6一起使用,这是撰写本文时的最新发布版本.为此,您应该创建代码的自定义派生.也欢迎拉动请求.

Disclaimer: since the answer uses private implementation details of TableRenderer it may not work in the future. It works with 7.1.6 which is the latest released version at the moment of writing this post. You should create a custom fork of the code for such purposes. Pull requests are welcome as well.

如果我们查看TableRenderer的实现,我们会看到该类包含heights( https://github.com/itext/itext7 ),将这些字段设置为protected并在我们的子类中使用它们.

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. 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.

您可以在代码中使用反射代替,但后果自负,但不应,因为它可能不适用于您的JVM(强烈建议不要更改可访问性修饰符),或者可能不起作用,下一版本的iText,或者由于其他原因可能无法正常工作.我不会在答案中添加反射代码,以进一步阻止其使用.

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. I will not add reflection code in my answer to further discourage its usage.

我们所需要做的就是覆盖drawBorders()方法.这是已经为行添加一些随机性的代码.

All we need to do it override drawBorders() method. 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);

结果表如下:

这篇关于绘制表格的自定义边框,在itext7中具有更大的灵活性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆