Version 2 (modified by sena, 7 years ago) (diff)


PDF Generation in Experiment Designer with Apache's PDFBox

Experiment Designer implements report generation with PDFBox. There is an exported package (org.grits.toolbox.editor.experimentdesigner.pdfgeneration) that provides methods to add text and tables into a PDF document.

ExperimentDesignRDFReport (org.grits.toolbox.editor.experimentdesigner.commands) class creates a PDF document using the methods provided in above package and saves it into a file chosen by the user. You can look into this class for an example of how to add regular text, multi-line text and tables into a document. There are a few points to consider while generating the PDF document. You have to keep track of the offset position (y index) and pass it to appropriate methods to position the cursor while drawing anything to the document. This index should initially be set to the length of the available writing area (for each new page). Then it will be decremented by the length of the row (plus paragraph spacing) each time a line of text is added. Therefore it is important to get the updated value of this offset every time a line of text is inserted. Example from ExperimentDesignRDFReport:

     // create a page for the project information
     PDPage page = new PDPage(PDPage.PAGE_SIZE_LETTER);
     document.addPage( page );
     float yIndex = LENGTH;  
     yIndex = PDFGenerator.writeSimpleText(document, page, "Project Information", null, BOLD_FONT, FONT_SIZE, MARGIN, yIndex);
     yIndex = PDFGenerator.writeSimpleText(document, page, "Name", project.getDisplayName(), TEXT_FONT, FONT_SIZE, MARGIN, yIndex);
     yIndex = PDFGenerator.writeSimpleText(document, page, "Created By", projectProperty.getCreator(), TEXT_FONT, FONT_SIZE, MARGIN, yIndex);
     yIndex = PDFGenerator.writeSimpleText(document, page, "Date Created", project.getCreationDate().toString(), TEXT_FONT, FONT_SIZE, MARGIN, yIndex);
     if (projectProperty.getDescription() != null) {
	yIndex = PDFGenerator.drawMultilineString(document, page, TEXT_FONT, FONT_SIZE, "Description: " + projectProperty.getDescription(), yIndex, MARGIN, LENGTH);

        // go to the last page
	page = (PDPage) document.getDocumentCatalog().getAllPages().get(document.getNumberOfPages()-1);

PDFGenerator.drawMultilineString must be used for any text field that might contain line breaks or a text that might span several lines in the output document (such as "description" above). Another essential step when using drawMultiLineString is to reinitialize the current page (you have to pass this page to all the methods that adds text to the document) to the last page of the document since while drawing several lines of text, a new page might have been created. This step is shown in the above code.

Table Generation

In order to create a table in the document, you need to create the columns, content of the table and calculate the maximum row height of the table (if table cells may span multiple rows).

This steps are shown below:

  1. Creating columns:
   List<Column> columns = new ArrayList<Column>();
   columns.add(new Column("Parameter/Parameter Group", 200));
   columns.add(new Column("Value", 100));
   columns.add(new Column("Unit", 100));
  1. Creating the content:
    String [][] content;
    // calculate the size of the table
    int tableSize=0;
    if (((ProtocolNode) node).getParameterGroups() != null) {
   	tableSize += ((ProtocolNode) node).getParameterGroups().size();
 	for (Iterator iterator2 = ((ProtocolNode) node).getParameterGroups().iterator(); iterator2
        	.hasNext();) {
		ParameterGroup group = (ParameterGroup);
		tableSize += group.getParameters().size();
    if (((ProtocolNode) node).getParameters() != null) {
    	tableSize += ((ProtocolNode) node).getParameters().size();
    content = new String[tableSize][3];
    // fill the contents
    int row = 0;
    float maxRowHeight = PDFGenerator.ROW_HEIGHT;
    if (((ProtocolNode) node).getParameterGroups() != null) {
       for (Iterator iterator2 = ((ProtocolNode) node).getParameterGroups().iterator(); iterator2.hasNext();) {
	   ParameterGroup group = (ParameterGroup);
  	   content [row][0] = group.getLabel();
	   content [row][1] = "";
	   content [row++][2] = "";
	   List<Parameter> parameters =  group.getParameters();
 	   for (Iterator iterator3 = parameters.iterator(); iterator3.hasNext();) {
		Parameter parameter = (Parameter);
		content[row][0] = "     " + parameter.getName();
		if (parameter.getValue() != null)  {
		    content[row][1] = parameter.getValue();
		    maxRowHeight = Math.max(PDFGenerator.calculateRowHeight(parameter.getValue(), 100, TEXT_FONT, FONT_SIZE, MARGIN), maxRowHeight);
		    content[row][1] = "";
		if (parameter.getUnit() != null)
		    content[row][2] = parameter.getUnit().getLabel();
		    content[row][2]= "";

  1. Calculate the Maximum row height:
     maxRowHeight = Math.max(PDFGenerator.calculateRowHeight(parameter.getValue(), 100, TEXT_FONT, FONT_SIZE, MARGIN), maxRowHeight);

This line shows how to calculate the expected row height for a given text. You should call this method whenever your cell text may span multiple lines. In the above example, parameter names and the units are unlikely to span multiple lines so calculation is done only for parameter values. This value is going to be used to draw the table grid to accommodate the largest row.

Building the table and drawing is done as follows:

float tableHeight = IS_LANDSCAPE ? PAGE_SIZE.getWidth() - (2 * MARGIN) : yIndex - (2 * MARGIN);
float tableMaxHeight = IS_LANDSCAPE ? PAGE_SIZE.getWidth() - (2 * MARGIN) : PAGE_SIZE.getHeight() - (2 * MARGIN);

Table table = new TableBuilder()

PDFGenerator tableGenerator = new PDFGenerator();

yIndex = tableGenerator.drawTable(document, protocolPage, table, true, yIndex);   
// go to the last page
protocolPage = (PDPage) document.getDocumentCatalog().getAllPages().get(document.getNumberOfPages()-1);