Sunday, October 10, 2010

Creating tables with PDFBox

Apache PDFBox is a useful Java library for working with PDF documents. It allows you to create new PDF documents and extract data from existing documents.

However, the library doesn't provide an API for creating tables within PDF documents. So I wrote my own method which uses basic operations like drawLine to draw the table cells and drawString to fill in the content. The code is shown below. Note, that this code could be improved to handle longer strings of text properly by wrapping text within cells, which it doesn't do at present.

/**
 * @param page
 * @param contentStream
 * @param y the y-coordinate of the first row
 * @param margin the padding on left and right of table
 * @param content a 2d array containing the table data
 * @throws IOException
 */
public static void drawTable(PDPage page, PDPageContentStream contentStream,
                            float y, float margin,
                            String[][] content) throws IOException {
    final int rows = content.length;
    final int cols = content[0].length;
    final float rowHeight = 20f;
    final float tableWidth = page.findMediaBox().getWidth()-(2*margin);
    final float tableHeight = rowHeight * rows;
    final float colWidth = tableWidth/(float)cols;
    final float cellMargin=5f;

    //draw the rows
    float nexty = y ;
    for (int i = 0; i <= rows; i++) {
        contentStream.drawLine(margin,nexty,margin+tableWidth,nexty);
        nexty-= rowHeight;
    }

    //draw the columns
    float nextx = margin;
    for (int i = 0; i <= cols; i++) {
        contentStream.drawLine(nextx,y,nextx,y-tableHeight);
        nextx += colWidth;
    }

    //now add the text
    contentStream.setFont(PDType1Font.HELVETICA_BOLD,12);

    float textx = margin+cellMargin;
    float texty = y-15;
    for(int i = 0; i < content.length; i++){
        for(int j = 0 ; j < content[i].length; j++){
            String text = content[i][j];
            contentStream.beginText();
            contentStream.moveTextPositionByAmount(textx,texty);
            contentStream.drawString(text);
            contentStream.endText();
            textx += colWidth;
        }
        texty-=rowHeight;
        textx = margin+cellMargin;
    }
}

public static void main(String[] args){
    PDDocument doc = new PDDocument();
    PDPage page = new PDPage();
    doc.addPage( page );

    PDPageContentStream contentStream =
                    new PDPageContentStream(doc, page);

    String[][] content = {{"a","b", "1"},
                          {"c","d", "2"},
                          {"e","f", "3"},
                          {"g","h", "4"},
                          {"i","j", "5"}} ;

    drawTable(page, contentStream, 700, 100, content);
    contentStream.close();
    doc.save("test.pdf" );
    }

14 comments:

  1. Anonymous4:35 PM

    Hi Shariff,

    Thanks for all the great effort in putting down under one shell.

    I tried the above code, having error with PDPageContentStream. drawline.

    Could you please update the full code that worked for you.



    Thanks,
    Naveen.

    ReplyDelete
  2. What error are you getting?

    ReplyDelete
  3. Anonymous7:09 PM

    the method drawline(float,float,float,float) is not defined for PDPageContentStream. And i tried to typecast it to Pagecontent stream. But looks that didnt work too.

    Thanks,
    naveen

    ReplyDelete
  4. Anonymous4:06 AM

    thanks worked like a charm.

    ReplyDelete
  5. Anonymous11:23 AM

    Hi Shariff,

    Programming baby here.
    The code works great but how can I wrap the text in the cells?

    Thank you.

    ReplyDelete
  6. Anonymous12:47 PM

    Thanks for this code it gave me a start :)

    @Anonymous2 : code wrapping is not that straightfwd.. wat we are doing here is drawing, Sophisticated table apis are available with other libraries like iText.

    ReplyDelete
  7. Hi Shariff

    Thanks for the info. But I'm a very beginner and asked to develop a 5 pages pdf having form with fields,background colour for all the pages,borders,various paragraphs,different side headings.Please share me any useful information or atleast links to worked examples having all the mentioned controls above. Thanks in advance.

    Chaitanya Kumar

    ReplyDelete
  8. I created a small api for creating tables using PDFBox. It can be found on github ( https://github.com/dhorions/boxable ) .

    A sample of a generated pdf can be found here http://goo.gl/a7QvRM.

    Any hints or suggestions are welcome.

    ReplyDelete
    Replies
    1. Can you Add the Functionality to Add an Image in your API as well. An easy way to add an image to a Table Cell.

      Delete
  9. This is a wonderful resource! Thank you for that package!

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. This code is wonderful and resourceful. Thank you ;-)

    ReplyDelete
  12. and create more than one table per page?

    ReplyDelete
    Replies
    1. Sir, i want separate column width for each column...how can i sir?please help me...i am trying...i am modifying colWidth field to tableHeight/float(5)...it is increasing the column width for all columns sir

      Delete