一个具有不同边框类型的单元格

时间:2016-01-01 11:34:34

标签: pdf-generation itext

当我尝试创建虚线单元格时,我正在使用example中所述的PdfPCellEvent。但是我得到了用虚线绘制的所有边框。当我设置cell.setBorder(Rectangle.BOTTOM | Rectangle.RIGHT);底部和右边用实线绘制。当我删除setBorder时,我将所有边框都点缀起来。

问题:

  1. 如何使用实心,点缀和NO_BORDER三种类型的边框绘制单元格。我正在从图片中看到一个示例性的简单表格。蓝线代表NO_BORDER。
  2. 当两个单元共享一个边界时,虚线重叠并组合成实线。避免它的唯一方法是从其中一个单元格中删除共享边框? How to draw such table cells where blue lines means no boder

1 个答案:

答案 0 :(得分:2)

我认为你正在混淆事情。

如果您使用单元格事件绘制边框,则创建自定义边框,您应删除所有自动边框。所以你总是需要:

 cell.setBorder(PdfPCell.NO_BORDER);

如果要绘制部分边框,则需要绘制部分边框。

在您引用的示例中,您使用moveTo()lineTo()stroke()命令序列添加了许多行。换句话说:决定绘制哪些行。

是画线的时候,很难理解为什么你会抱怨你“画出所有的边框”。如果要减少绘制的边框数,请绘制较少的线条。

你应该省略哪一行?这不是我们自己决定的:你知道你需要什么;你应该决定!

请查看DottedLineCell2示例。这是您所引用示例的变体。在此示例中,我创建了一个分别绘制每个边框的事件:

class DottedCell implements PdfPCellEvent {
    private int border = 0;
    public DottedCell(int border) {
        this.border = border;
    }
    public void cellLayout(PdfPCell cell, Rectangle position,
        PdfContentByte[] canvases) {
        PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
        canvas.saveState();
        canvas.setLineDash(0, 4, 2);
        if ((border & PdfPCell.TOP) == PdfPCell.TOP) {
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getTop());
        }
        if ((border & PdfPCell.BOTTOM) == PdfPCell.BOTTOM) {
            canvas.moveTo(position.getRight(), position.getBottom());
            canvas.lineTo(position.getLeft(), position.getBottom());
        }
        if ((border & PdfPCell.RIGHT) == PdfPCell.RIGHT) {
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getRight(), position.getBottom());
        }
        if ((border & PdfPCell.LEFT) == PdfPCell.LEFT) {
            canvas.moveTo(position.getLeft(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getBottom());
        }
        canvas.stroke();
        canvas.restoreState();
    }
}

创建此事件的实例时,您必须传递border值。在cellLayout()方法中,我们将查看此边框值:

  • 当选择TOP位时,我们构建一条从右上角到左上角的线,
  • 当选择BOTTOM位时,我们构建一条从右下角到左下角的线,
  • 当选择RIGHT位时,我们构建一条从右上角到右下角的线,
  • 当选择LEFT位时,我们会从左上角到左下角构建一条线。

一旦我们检查了边框的所有边,我们stroke()就行了。

在以下示例中,我创建了两个表:

public void createPdf(String dest) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(dest));
    document.open();

    PdfPTable table;
    PdfPCell cell;

    table = new PdfPTable(4);
    table.setSpacingAfter(30);
    cell = new PdfPCell(new Phrase("left border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.LEFT));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("right border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.RIGHT));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("top border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.TOP));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("bottom border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.BOTTOM));
    table.addCell(cell);
    document.add(table);

    table = new PdfPTable(4);
    table.setSpacingAfter(30);
    cell = new PdfPCell(new Phrase("left and top border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.LEFT | PdfPCell.TOP));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("right and bottom border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.RIGHT | PdfPCell.BOTTOM));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("no border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("full border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new DottedCell(PdfPCell.BOX));
    table.addCell(cell);
    document.add(table);
    document.close();
}

这两个表格如下:dotted_line_cell2.pdf

enter image description here

更新2

在你的评论中,你声称我的答案是不够的,解释说你的编程技巧相当有限:你不能将单元格事件调整为绘制不同类型边框的事件。关于我最初的例子,你也问“这是唯一的方法吗?”

当然,这不是唯一的方法:有许多不同的方式来达到你想要的结果。请允许我提出两个额外的例子(尽管有更多可能的变体,其中一些可能涉及表事件而不是单元格事件):

额外示例#1:使用界面定义行短划线:

CustomBorder3示例中,我复制/粘贴了上一个示例中的单元格事件,并按照以下方式对其进行了修改:

class CustomBorder implements PdfPCellEvent {
    protected LineDash left;
    protected LineDash right;
    protected LineDash top;
    protected LineDash bottom;
    public CustomBorder(LineDash left, LineDash right,
            LineDash top, LineDash bottom) {
        this.left = left;
        this.right = right;
        this.top = top;
        this.bottom = bottom;
    }
    public void cellLayout(PdfPCell cell, Rectangle position,
        PdfContentByte[] canvases) {
        PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
        if (top != null) {
            canvas.saveState();
            top.applyLineDash(canvas);
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getTop());
            canvas.stroke();
            canvas.restoreState();
        }
        if (bottom != null) {
            canvas.saveState();
            bottom.applyLineDash(canvas);
            canvas.moveTo(position.getRight(), position.getBottom());
            canvas.lineTo(position.getLeft(), position.getBottom());
            canvas.stroke();
            canvas.restoreState();
        }
        if (right != null) {
            canvas.saveState();
            right.applyLineDash(canvas);
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getRight(), position.getBottom());
            canvas.stroke();
            canvas.restoreState();
        }
        if (left != null) {
            canvas.saveState();
            left.applyLineDash(canvas);
            canvas.moveTo(position.getLeft(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getBottom());
            canvas.stroke();
            canvas.restoreState();
        }
    }
}

如您所见,我不再定义border值,而是定义了四个值:leftrighttop和{{1} }。在bottom方法中,我为每个与cellLayout()不同的值绘制一条线。

变量属于null类型。 LineDash是一个具有单一方法的接口:

LineDash

我为这个界面创建了三个实现:

interface LineDash {
    public void applyLineDash(PdfContentByte canvas);
}

您可以轻松创建新的实施,例如:

class SolidLine implements LineDash {
    public void applyLineDash(PdfContentByte canvas) { }
}

class DottedLine implements LineDash {
    public void applyLineDash(PdfContentByte canvas) {
        canvas.setLineCap(PdfContentByte.LINE_CAP_ROUND);
        canvas.setLineDash(0, 4, 2);
    }
}

class DashedLine implements LineDash {
    public void applyLineDash(PdfContentByte canvas) {
        canvas.setLineDash(3, 3);
    }
}

你甚至可以引入一种改变边框颜色和宽度的实现。

我现在可以像这样使用class DashedLine2 implements LineDash { float unitsOn; float phase; public DashedLine2(float unitsOn, float phase) { this.unitsOn = unitsOn; this.phase = phase; } public void applyLineDash(PdfContentByte canvas) { canvas.setLineDash(unitsOn, phase); } } 事件:

CustomBorder

结果如下:

enter image description here

额外示例#2:使用抽象类作为单元格事件的基础:

CustomBorder4示例中,我复制/粘贴了上一个示例中的单元格事件,并按照以下方式对其进行了修改:

public void createPdf(String dest) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(dest));
    document.open();

    PdfPTable table;
    PdfPCell cell;
    LineDash solid = new SolidLine();
    LineDash dotted = new DottedLine();
    LineDash dashed = new DashedLine();

    table = new PdfPTable(4);
    table.setSpacingAfter(30);
    cell = new PdfPCell(new Phrase("dotted left border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(dotted, null, null, null));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("solid right border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(null, solid, null, null));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("dashed top border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(null, null, dashed, null));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("bottom border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(null, null, null, solid));
    table.addCell(cell);
    document.add(table);

    table = new PdfPTable(4);
    table.setSpacingAfter(30);
    cell = new PdfPCell(new Phrase("dotted left and solid top border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(dotted, null, solid, null));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("dashed right and dashed bottom border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(null, dashed, null, dashed));
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("no border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    table.addCell(cell);
    cell = new PdfPCell(new Phrase("full solid border"));
    cell.setBorder(PdfPCell.NO_BORDER);
    cell.setCellEvent(new CustomBorder(solid, solid, solid, solid));
    table.addCell(cell);
    document.add(table);
    document.close();
}

这个类是抽象的,因为它包含一个未实现的方法。我现在可以像这样扩展这个抽象类:

abstract class CustomBorder implements PdfPCellEvent {
    private int border = 0;
    public CustomBorder(int border) {
        this.border = border;
    }
    public void cellLayout(PdfPCell cell, Rectangle position,
        PdfContentByte[] canvases) {
        PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];
        canvas.saveState();
        setLineDash(canvas);
        if ((border & PdfPCell.TOP) == PdfPCell.TOP) {
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getTop());
        }
        if ((border & PdfPCell.BOTTOM) == PdfPCell.BOTTOM) {
            canvas.moveTo(position.getRight(), position.getBottom());
            canvas.lineTo(position.getLeft(), position.getBottom());
        }
        if ((border & PdfPCell.RIGHT) == PdfPCell.RIGHT) {
            canvas.moveTo(position.getRight(), position.getTop());
            canvas.lineTo(position.getRight(), position.getBottom());
        }
        if ((border & PdfPCell.LEFT) == PdfPCell.LEFT) {
            canvas.moveTo(position.getLeft(), position.getTop());
            canvas.lineTo(position.getLeft(), position.getBottom());
        }
        canvas.stroke();
        canvas.restoreState();
    }

    public abstract void setLineDash(PdfContentByte canvas);
}

同样,我可以引入不同的参数来改变短划线图案,颜色,线宽等。但这是你可以轻松自己做的事情。

我现在有三种不同的细胞事件,我可以在不同细胞或同一细胞上使用:

class SolidBorder extends CustomBorder {
    public SolidBorder(int border) { super(border); }
    public void setLineDash(PdfContentByte canvas) {}
}
class DottedBorder extends CustomBorder {
    public DottedBorder(int border) { super(border); }
    public void setLineDash(PdfContentByte canvas) {
        canvas.setLineCap(PdfContentByte.LINE_CAP_ROUND);
        canvas.setLineDash(0, 4, 2);
    }
}
class DashedBorder extends CustomBorder {
    public DashedBorder(int border) { super(border); }
    public void setLineDash(PdfContentByte canvas) {
        canvas.setLineDash(3, 3);
    }
}

结果如下:

enter image description here

这些只是 两个额外的例子。人们可以轻松写出更多内容。如果您有任何进一步的问题(例如:如何更改边框的颜色),请创建一个新问题并显示您编写的代码,解释为什么它没有为您提供您期望的结果。