Skip to content
ljpeixoto edited this page Oct 24, 2020 · 27 revisions

Welcome to the boxable wiki!

FAQ

Creating text,image,table with boxable

Okay, first things first. What we always need is our PDDocument and PDPage :

PDPage myPage = new PDPage(PDRectangle.A4);
PDDocument mainDocument = new PDDocument();

If we want to add something else in our PDF document except tables (i.e some text, image etc.) then we will need content stream:

PDPageContentStream contentStream = new PDPageContentStream(mainDocument, myPage);

Text (outside of the table)

That is something that we always need. Now, how about random text in PDF document, i.e our document title

contentStream.beginText();
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 22);
contentStream.newLineAtOffset(50, 700);
contentStream.showText("Document title");
contentStream.endText();

Also it is pretty easy to write some text inside PDF Document with PDStreamUtils.write(final PDPageContentStream stream, final String text, final PDFont font, final float fontSize, final float x, final float y, final Color color) method where :

  • text - The text which will be displayed.
  • font - The font of the text
  • fontSize - The font size of the text
  • x - Start X coordinate for text.
  • y - Start Y coordinate for text.
  • color - Color of the text

Maybe it is better to put that in some context, here is second example how to write document title with method mention above.

[...]
private PDPageContentStream cos;
private PDPage page;
private PDFont font = PDType1Font.HELVETICA;
private float leftMargin = 50;
private marginBetweenYElements = 10;
private float titleFontSize = 18;
[...]
private void drawPageTitle() throws IOException {
        // draw document title first
        PDStreamUtils.write(cos, "Document title", font, titleFontSize, leftMargin, yPosition,
                Color.BLACK);

        // drop Y position with default margin between vertical elements
        yPosition -= marginBetweenYElements;
    }

Table

What about simple table? Say no more:

//Dummy Table
    float margin = 50;
// starting y position is whole page height subtracted by top and bottom margin
    float yStartNewPage = myPage.getMediaBox().getHeight() - (2 * margin);
// we want table across whole page width (subtracted by left and right margin ofcourse)
    float tableWidth = myPage.getMediaBox().getWidth() - (2 * margin);

    boolean drawContent = true;
    float yStart = yStartNewPage;
    float bottomMargin = 70;
// y position is your coordinate of top left corner of the table
    float yPosition = 550;

    BaseTable table = new BaseTable(yPosition, yStartNewPage, bottomMargin, tableWidth, margin, mainDocument, myPage, true, drawContent);


    Row<PDPage> headerRow = table.createRow(15f);
    Cell<PDPage> cell = headerRow.createCell(100, "Header");
    table.addHeaderRow(headerRow);


    Row<PDPage> row = table.createRow(12);
    cell = row.createCell(30, "Data 1");
    cell = row.createCell(70, "Some value");

    table.draw();


    contentStream.close();
    mainDocument.addPage(myPage);
    mainDocument.save("testfile.pdf");
    mainDocument.close();

Image

It is very similar process just like writing text. All work is done by ''Ìmage.draw(final PDDocument doc, final PDPageContentStream stream, float x, float y)'' method where:

  • doc - PDDocument where drawing will be applied
  • stream - PDPageContentStream where drawing will be applied
  • x - X coordinate for image drawing
  • y - Y coordinate for image drawing

Lets put that in context, for example, drawing logo on our PDF document:

[...]
Image image = new Image(ImageIO.read(new File("/../logo.png")));
// imagine we have pretty big logo and we want scale that a little bit
float imageWidth = 75;
image = image.scaleByWidth(imageWidth);
image.draw(document, contentStream, xPosition, yPosition)

Important Always close your contentStream before saving the document!

[...]
contentStream.close();
// Save the document
File file = new File("test.pdf");
 System.out.println("Sample file saved at : " + file.getAbsolutePath());
 Files.createParentDirs(file);
 doc.save(file);
 doc.close();

If you don’t do this you will be getting something like:

java.lang.IllegalStateException: Cannot read while there is an open stream writer
    at org.apache.pdfbox.cos.COSStream.createRawInputStream(COSStream.java:128)
[...]

Some other interesting methods for drawing and image scaling are:

draw(final PDDocument doc, final PDPageContentStream stream, float x, float y)
scaleByWidth(float width)
scaleByHeight(float height)
scale(float boundWidth, float boundHeight)

And that’s it! You are ready to go!

Landscape (A4)

PDPage page = new PDPage(new PDRectangle(PDRectangle.A4.getHeight(), PDRectangle.A4.getWidth()));

Portrait (A4)

PDPage page = new PDPage(PDRectange.A4);

How to get table height dynamically?

float tableHeight = table.getHeaderAndDataHeight();

IMPORTANT: Use this method witch caution! Method doesn't acknowledge possible page break !!

Retrieving current table page

If table is displayed on multiple pages the current page can be obtained with table.getCurrentPage(). Something like :

[...]
// did we change the page?
if (table.getCurrentPage() != page) {
    cos.close();
    page = table.getCurrentPage();
    cos = new PDPageContentStream(document, page, true, true);
}

Retrieving current y position after multipage table

It's pretty straightforward as:

yStart = table.draw();

This gives you yStart position exactly on the end of table so it would be better to secure more space for drawing another table,text,image with additional, let's say 50 --> yStart = table.draw() - 50;

How to get two tables with same y position?

Let's say that we want two tables by each other (pay attention on spaceBetweenTables variable and x/y positioning of the tables!)

                int startNewPageY = 700;
		int bottomMargin = 100;
		int leftMargin = 25;
		PDPage currentPage = new PDPage();
                int spaceBetweenTables = 50;
	
		// we want 2 tables so our table width is 50% of page width without left and right margin AND provided space between tables
		float tableWidth = 0.5f * (currentPage.getMediaBox().getWidth() - (2 * leftMargin)- spaceBetweenTables);
		PDDocument document = new PDDocument();
		document.addPage(currentPage);

		BaseTable table1 = new BaseTable(700, startNewPageY, bottomMargin, tableWidth, leftMargin, document,
				currentPage, true, true);
                [...]
                table1.draw();
                // pay attention where start x position for this table -> left margin + first table width + space between our tables
		BaseTable table2 = new BaseTable(700, startNewPageY, bottomMargin, tableWidth, leftMargin + tableWidth + spaceBetweenTables, document,
				currentPage, true, true);
                [...]
                table2.draw();

(Same principle is if you go with DataTable implementation)

Then we should get something like this:

Two tables

The same principle is for multiple tables where we need only to adjust tableWidth variable and BaseTable's argument that is responsible for x positioning.

Making inner table

The best way to make inner table is using TableCell class with related HTML tags (<table>, <tr>, <td>)

Something like (making 2x2 inner table):

Cell<PDPage> cell = row.createTableCell((100 / 3f),"<table><tr><td>First row, first value</td><td>First row, second value</td></tr><tr><td>Second row, first value</td><td>Second row, second value</td></tr></table>",
doc, page, yStart, topMargin, bottomMargin);

And that's it! You just created inner 2x2 table. If you want to see more please check SampleTest10() for more.

Here is small PDF output from SampleTest10() inner table