vandeseer / easytable

Small table drawing library built upon Apache PDFBox
MIT License
246 stars 94 forks source link

Table does not render properly when using colspan and relatively large data #96

Open pradhan-v opened 4 years ago

pradhan-v commented 4 years ago

The table renders fine when using small strings in the cells. But, when the data in the cells are large...

Please execute the following code to see the issue.

public class EasytableTest2 {

    private static final String FILE_NAME = "excelLike.pdf";
    private static final float PADDING = 50f;

    public static void main(String[] args) throws IOException {

        createAndSaveDocumentWithTables(new PDDocument(), FILE_NAME, createRegularTable(false),
                createRegularTable(true));
    }

    private static void createAndSaveDocumentWithTables(PDDocument document, String outputFileName, Table... tables)
            throws IOException {

        final PDPage page = new PDPage(PDRectangle.A4);
        document.addPage(page);
        float startY = page.getMediaBox().getHeight() - PADDING;

        try (final PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
            for (final Table table : tables) {
                TableDrawer.builder()//
                        .page(page)//
                        .contentStream(contentStream)//
                        .table(table)//
                        .startX(PADDING)//
                        .startY(startY)//
                        .endY(PADDING)//
                        .build()//
                        .draw(() -> document, () -> new PDPage(PDRectangle.A4), PADDING);
                startY -= (table.getHeight() + PADDING);
            }
        }
        document.save(outputFileName);
        document.close();
    }

    private static Table createRegularTable(boolean large) {

        return Table.builder().addColumnsOfWidth(150, 150, 150).borderWidth(0.25f)//
                .addRow(Row.builder()//
                        .add(TextCell.builder().text("head 1").backgroundColor(Color.LIGHT_GRAY).build())//
                        .add(TextCell.builder().text("head 2").backgroundColor(Color.LIGHT_GRAY).build())//
                        .add(TextCell.builder().text("head 3").backgroundColor(Color.LIGHT_GRAY).build())//
                        .build())//
                .addRow(Row.builder()//
                        .add(TextCell.builder().text("row span 4").rowSpan(4).build())//
                        .add(TextCell.builder().text("row span 2 1").rowSpan(2).build())//
                        .add(TextCell.builder().text(dummyData("data 1", large)).build())//
                        .build())//
                .addRow(Row.builder()//
                        .add(TextCell.builder().text(dummyData("data 2", large)).build()).build())//
                .addRow(Row.builder()//
                        .add(TextCell.builder().rowSpan(2).text("row span 2 2").build())//
                        .add(TextCell.builder().text(dummyData("data 3", large)).build())//
                        .build())//
                .addRow(Row.builder()//
                        .add(TextCell.builder().text(dummyData("data 4", large)).build())//
                        .build())//
                .build();
    }

    private static String dummyData(String str, boolean gen) {
        if (!gen) {
            return str;
        }
        StringBuilder sb = new StringBuilder(str);
        for (int i = 0; i < 50; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
}
vandeseer commented 4 years ago

Hi @pradhan-v,

Indeed: that doesn't look very nice. The problem here is that the first row is too big: since the cells on the right are increasing the size of the cell on the left (the one with rowspan) and since the first row is now too big to be drawn on one page it doesn't render correctly.

The question is: what would be a correct rendering? We would need to somehow split the cell on the left over the page border. But there is no concept of cell splitting in easytable.

The "fix" will most probably be that an exception will be thrown in such cases as is already if a single cell is too big.

Anyway: You could work around by using regular cells on the left side that have no borders in between i.e. setting border width to 0 for bottom/top borders.

Hope this helps, Stefan

pradhan-v commented 4 years ago

Hi @vandeseer

Throwing an exception also would leave the rowSpan feature unusable :(. May be draw the cell with the height set to the table height (endY?). Carry over the remaining height to the next page ?

Setting the top/bottom border widths to 0 does the trick. But, it leaves "holes" at the bottom of the table when the "row spanned" cells span across multiple pages. This is okay, for now.

Thanks. Pradhan.