How to define different border types for a single cell?

Tags: tablesdotted linecell borderiText 7
Example table
Example table

When I set up the cell.setBorder(Rectangle.BOTTOM | Rectangle.RIGHT); the bottom and right get drawn with solid line. When I remove the setBorder() I get all borders dotted.

  1. How can I draw a cell with three types of borders solid, dotted and NO_BORDER. I want to create a simple table as shown in the image below, where the blue lines stands for NO_BORDER.
  2. If two cells share one border, the dotted line overlaps and combines into solid line. Is the only way to avoid it is to remove the shared border form one of the cells?

Example table

Question posted on StackOverflow on Jan 1, 2016 by mCs

I think you are mixing up things.

If you use CellRenderer to draw borders, you create custom borders, you should remove all automated borders. So you always need:

 cell.setBorder(Border.NO_BORDER);

If you want to draw partial borders, you need to draw partial borders.

When you add a border using a cell renderer, you add a number of lines using a sequence of moveTo(), lineTo() and stroke() commands. In other words: you decide which lines are drawn. If you want to reduce the number of borders drawn, you need to draw less lines.

Please take a look at the DottedLineCell2 example. This is a variation on the example you refer to. In this example, I create a renderer that draws each border separately:

class DottedLineCellRenderer extends CellRenderer {

  1. boolean[] borders;
  1. public DottedLineCellRenderer(Cell modelElement, boolean[] borders) {
  2. super(modelElement);
  3. this.borders = new boolean[borders.length];
  4. for (int i = 0; i < this.borders.length; i++) {
  5. this.borders[i] = borders[i];
  6. }
  7. }
  8.  
  9. @Override
  10. public void draw(DrawContext drawContext) {
  11. super.draw(drawContext);
  12. PdfCanvas canvas = drawContext.getCanvas();
  13. Rectangle position = getOccupiedAreaBBox();
  14. canvas.saveState();
  15. canvas.setLineDash(0, 4, 2);
  16. if (borders[0]) {
  17. canvas.moveTo(position.getRight(), position.getTop());
  18. canvas.lineTo(position.getLeft(), position.getTop());
  19. }
  20. if (borders[2]) {
  21. canvas.moveTo(position.getRight(), position.getBottom());
  22. canvas.lineTo(position.getLeft(), position.getBottom());
  23. }
  24. if (borders[3]) {
  25. canvas.moveTo(position.getRight(), position.getTop());
  26. canvas.lineTo(position.getRight(), position.getBottom());
  27. }
  28. if (borders[1]) {
  29. canvas.moveTo(position.getLeft(), position.getTop());
  30. canvas.lineTo(position.getLeft(), position.getBottom());
  31. }
  32. canvas.stroke();
  33. canvas.restoreState();
  34. }

}

You have to pass a value for each border and in the draw() method, we'll look at this border value:

  • When the first element of array has True value ( TOP ), we construct a line from the upper-right corner to the upper-left corner,

  • When the second element of array has True value (BOTTOM), we construct a line from the lower-right corner to the lower-left corner,

  • When the third element of array has True value (RIGHT), we construct a line from the upper-right corner to the lower-right corner,

  • When the fourth element of array has True value (LEFT), we construct a line from the upper-left corner to the lower-left corner.

Once we've checked all the sides of the border, we stroke() the lines.

In the following example, I create two tables:

public void manipulatePdf(String dest) throws Exception { PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest)); Document document = new Document(pdfDoc); Table table;

  1. Cell cell;
  1. table = new Table(4);
  2. table.setMarginBottom(30);
  3. cell = new Cell().add("left border");
  4. cell.setBorder(Border.NO_BORDER);
  5. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  6. new boolean[]{false, true, false, false}));
  7. table.addCell(cell);
  8. cell = new Cell().add("right border");
  9. cell.setBorder(Border.NO_BORDER);
  10. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  11. new boolean[]{false, false, false, true}));
  12. table.addCell(cell);
  13. cell = new Cell().add("top border");
  14. cell.setBorder(Border.NO_BORDER);
  15. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  16. new boolean[]{true, false, false, false}));
  17. table.addCell(cell);
  18. cell = new Cell().add("bottom border");
  19. cell.setBorder(Border.NO_BORDER);
  20. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  21. new boolean[]{false, false, true, false}));
  22. table.addCell(cell);
  23. document.add(table);
  24.  
  25. table = new Table(4);
  26. table.setMarginBottom(30);
  27. cell = new Cell().add("left and top border");
  28. cell.setBorder(Border.NO_BORDER);
  29. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  30. new boolean[]{true, true, false, false}));
  31. table.addCell(cell);
  32. cell = new Cell().add("right and bottom border");
  33. cell.setBorder(Border.NO_BORDER);
  34. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  35. new boolean[]{false, false, true, true}));
  36. table.addCell(cell);
  37. cell = new Cell().add("no border");
  38. cell.setBorder(Border.NO_BORDER);
  39. table.addCell(cell);
  40. cell = new Cell().add("full border");
  41. cell.setBorder(Border.NO_BORDER);
  42. cell.setNextRenderer(new DottedLineCellRenderer(cell,
  43. new boolean[]{true, true, true, true}));
  44. table.addCell(cell);
  45. document.add(table);
  46. document.close();

This is what the two tables look like: dotted_line_cell2.pdf

Table cells with different border types
Table cells with different border types

Update:

You can't adapt the cell renderer into a renderer that draws different types of borders.

Example shown above is not the only way: there are many different ways to achieve the result you desire. Allow me to present one more example :

Extra example: Using an interface to define the line dash:

In the CustomBorder3 example, I copy/pasted the cell renderer from my previous example and I adapted it like this:

class CustomBorder3Renderer extends CellRenderer {

  1. ILineDash[] borders;
  1. public CustomBorder3Renderer(Cell modelElement, ILineDash[] borders) {
  2. super(modelElement);
  3. this.borders = new ILineDash[borders.length];
  4. for (int i = 0; i < this.borders.length; i++) {
  5. this.borders[i] = borders[i];
  6. }
  7. }
  8.  
  9. @Override
  10. public void draw(DrawContext drawContext) {
  11. super.draw(drawContext);
  12. PdfCanvas canvas = drawContext.getCanvas();
  13. Rectangle position = getOccupiedAreaBBox();
  14. canvas.saveState();
  15. if (null != borders[0]) {
  16. canvas.saveState();
  17. borders[0].applyLineDash(canvas);
  18. canvas.moveTo(position.getRight(), position.getTop());
  19. canvas.lineTo(position.getLeft(), position.getTop());
  20. canvas.stroke();
  21. canvas.restoreState();
  22. }
  23. if (null != borders[2]) {
  24. canvas.saveState();
  25. borders[2].applyLineDash(canvas);
  26. canvas.moveTo(position.getRight(), position.getBottom());
  27. canvas.lineTo(position.getLeft(), position.getBottom());
  28. canvas.stroke();
  29. canvas.restoreState();
  30. }
  31. if (null != borders[3]) {
  32. canvas.saveState();
  33. borders[3].applyLineDash(canvas);
  34. canvas.moveTo(position.getRight(), position.getTop());
  35. canvas.lineTo(position.getRight(), position.getBottom());
  36. canvas.stroke();
  37. canvas.restoreState();
  38. }
  39. if (null != borders[1]) {
  40. canvas.saveState();
  41. borders[1].applyLineDash(canvas);
  42. canvas.moveTo(position.getLeft(), position.getTop());
  43. canvas.lineTo(position.getLeft(), position.getBottom());
  44. canvas.stroke();
  45. canvas.restoreState();
  46. }
  47. canvas.stroke();
  48. canvas.restoreState();
  49. }

I draw a line for each of those values that is different from null.

The variables are of type ILineDash. ILineDash is an interface with a single method:

interface ILineDash {
    void applyLineDash(PdfCanvas canvas);
}

I created three implementations for this interface:

class Solid implements ILineDash {
    public void applyLineDash(PdfCanvas canvas) {
    }
}
class Dotted implements ILineDash {
    public void applyLineDash(PdfCanvas canvas) {
        canvas.setLineCapStyle(PdfCanvasConstants.LineCapStyle.ROUND);
        canvas.setLineDash(0, 4, 2);
    }
}
class Dashed implements ILineDash {
    public void applyLineDash(PdfCanvas canvas) {
        canvas.setLineDash(3, 3);
    }
}

You can easily create new implementations such as:

class DashedLine2 implements ILineDash {
    float unitsOn;
    float phase;
    public DashedLine2(float unitsOn, float phase) {
        this.unitsOn = unitsOn;
        this.phase = phase;
    }
    public void applyLineDash(PdfCanvas canvas) {
        canvas.setLineDash(unitsOn, phase);
    }
}

You could even introduce an implementation that changes the color and the width of the border.

I can now use the CustomBorder renderer like this:

public void manipulatePdf(String dest) throws Exception {
    PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
    Document document = new Document(pdfDoc);
 
    ILineDash solid = new Solid();
    ILineDash dotted = new Dotted();
    ILineDash dashed = new Dashed();
 
    Table table;
    Cell cell;
 
    table = new Table(4);
    table.setMarginBottom(30);
    cell = new Cell().add("dotted left border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{null, dotted, null, null}));
    table.addCell(cell);
    cell = new Cell().add("solid right border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{null, null, null, solid}));
    table.addCell(cell);
    cell = new Cell().add("dashed top border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{dashed, null, null, null}));
    table.addCell(cell);
    cell = new Cell().add("bottom border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{null, null, solid, null}));
    table.addCell(cell);
    document.add(table);
 
    table = new Table(4);
    table.setMarginBottom(30);
    cell = new Cell().add("dotted left and solid top border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{solid, dotted, null, null}));
    table.addCell(cell);
    cell = new Cell().add("dashed right and dashed bottom border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{null, null, dashed, dashed}));
    table.addCell(cell);
    cell = new Cell().add("no border");
    cell.setBorder(Border.NO_BORDER);
    table.addCell(cell);
    cell = new Cell().add("full solid border");
    cell.setBorder(Border.NO_BORDER);
    cell.setNextRenderer(new CustomBorder3Renderer(cell,
            new ILineDash[]{solid, solid, solid, solid}));
    table.addCell(cell);
    document.add(table);
    document.close();
}

The result looks like this:

Table cells with different border types
Table cells with different border types

Click this link if you want to see how to answer this question in iText 5.