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

时间:2019-05-12 17:40:14

标签: java pdf itext itext7

之前,我问过一个有关绘制自定义表格边框的问题:Draw custom borders for table spanning across more than one page in itext7

提供了一种绘制自定义边框的方法,但是它不允许影响完整的行或列线(例如,稍微倾斜它),因为每个单元格的边框都是单独绘制的。

我想在表格边框中添加一些随机性,如以下屏幕截图所示:

table borders

这是我的代码,适用于适合单个页面的表,但是如果表跨多个页面,则该代码将不再起作用:

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();
    }
}

我想自己绘制自定义边框:)

1 个答案:

答案 0 :(得分:2)

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

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

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

如果我们看一下heights的实现,就会发现该类包含countedColumnWidthline in code)和privateline in code)字段,听起来很有趣,但它们是protected。这意味着我们应该创建iText的自定义分支(源代码可在https://github.com/itext/itext7中使用),将这些字段设置为drawBorders(),并在子类中使用它们。

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

我们需要做的就是覆盖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(); } } 方法。这是已经为行添加一些随机性的代码。

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);

要激活自定义渲染器,请在添加到文档之前将其设置为表格:

     import time
     seconds = time.time();
     result = time.ctime(seconds);

     if(result.tm_year==y &&result.tm_mday==d &&result.tm_hour==h)
     {
     }

这是结果表的样子:

result