Apache PDFBox Table/Row Alignment with Boxable - java

I am exporting a PDF in my program, and I wanted to create a table with ApachePDF Box which should be about 50-60% of the Page width.
However, I didnt manage to find anything about centering the rows/the table itself.
I found how to align text in the row/cell itself, but if I create a Row that does not use the full width of the page, its always left aligned, and I dont know how to center align the row, since the row or table does not have a setAlign method.
Im using Boxable on top of it (https://github.com/dhorions/boxable)

public void Test() throws IOException {
//Set margins
float margin = 10;
//Initialize Document
PDDocument doc = new PDDocument();
PDPage page = addNewPage(doc);
//Initialize table
float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
float yStartNewPage = page.getMediaBox().getHeight() - (2 * margin);
boolean drawContent = true;
boolean drawLines = true;
float yStart = yStartNewPage;
float bottomMargin = 70;
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, doc, page, drawLines,
drawContent);
// set default line spacing for entire table
table.setLineSpacing(1.5f);
Row<PDPage> row = table.createRow(10);
// set single spacing for entire row
row.setLineSpacing(1f);
// my first 3x wider cell
Cell<PDPage> cell = row.createCell((3*100/15f), "1",
HorizontalAlignment.get("center"), VerticalAlignment.get("top"));
cell.setFontSize(6);
// my other 12 equal cells
for(int i=2; i<14; i++){
Cell<PDPage> cell2 = row.createCell((100/15f), String.valueOf(i),
HorizontalAlignment.get("center"), VerticalAlignment.get("top"));
cell2.setFontSize(6);
}
table.draw();
//Save the document
File file = new File("target/test.pdf");
System.out.println("Sample file saved at : " + file.getAbsolutePath());
Files.createParentDirs(file);
doc.save(file);
doc.close();
}
Adjust set position and table width....

Adjust set position and table width:
public void Test() throws IOException {
//Set margins
float margin = 10;
//Initialize Document
PDDocument doc = new PDDocument();
PDPage page = addNewPage(doc);
//Initialize table
float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
float yStartNewPage = page.getMediaBox().getHeight() - (2 * margin);
boolean drawContent = true;
boolean drawLines = true;
float yStart = yStartNewPage;
float bottomMargin = 70;
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, doc, page, drawLines, drawContent);
// set default line spacing for entire table
table.setLineSpacing(1.5f);
Row<PDPage> row = table.createRow(10);
// set single spacing for entire row
row.setLineSpacing(1f);
// my first 3x wider cell
Cell<PDPage> cell = row.createCell((3*100/15f), "1",
HorizontalAlignment.get("center"), VerticalAlignment.get("top"));
cell.setFontSize(6);
// my other 12 equal cells
for(int i=2; i<14; i++){
Cell<PDPage> cell2 = row.createCell((100/15f), String.valueOf(i),
HorizontalAlignment.get("center"), VerticalAlignment.get("top"));
cell2.setFontSize(6);
}
table.draw();
//Save the document
File file = new File("target/test.pdf");
System.out.println("Sample file saved at : " + file.getAbsolutePath());
Files.createParentDirs(file);
doc.save(file);
doc.close();
}

Related

How to create multiple table on the same page pdfbox?

I want to create multiple table(table below table) using pdfbox and boxable.
but table just overlap, how do I solve it?
for(ProductGroup productGroup: productGroups) {
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, doc, page, true, drawContent);
Row<PDPage> headerRow = table.createRow(15f);
Cell<PDPage> cell;
createHeader(headerRow, table);
Row<PDPage> row;
for(Article article: productGroup.getArticles()) {
row = table.createRow(10f);
cell = row.createCell((100 / 9f) , article.getBranch().replace("\n", "").replace("\r", ""));
cell.setFont(PDType1Font.HELVETICA);
cell.setFontSize(fontSize10);
cell = row.createCell((100 / 9f) , article.getMode().replace("\n", "").replace("\r", ""));
cell.setFont(PDType1Font.HELVETICA);
cell.setFontSize(fontSize10);
cell = row.createCell((100 / 3f) , article.getFeatureText().replace("\n", "").replace("\r", ""));
cell.setFont(PDType1Font.HELVETICA);
cell.setFontSize(fontSize10);
}
}
When you call table.draw() it returns yStart coordinate i.e. where the table ends on the current page. You can use it as a input parameter yStart for your next table in for loop.
For example in your code snippet
float yStart = 650;//for example
for(ProductGroup productGroup: productGroups) {
BaseTable table = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, doc, page, true, drawContent);
Row<PDPage> headerRow = table.createRow(15f);
Cell<PDPage> cell;
createHeader(headerRow, table);
Row<PDPage> row;
for(Article article: productGroup.getArticles()) {
row = table.createRow(10f);
cell = row.createCell((100 / 9f) , article.getBranch().replace("\n", "").replace("\r", ""));
.....
}
//getting Yposition of table end
yStart = table.draw();
//As you are drawing multiple tables which might get split over pages, get the latest page
page = document.getPage(document.getNumberOfPages()-1);
}
Set, yStart = table.draw()
Create another BaseTable with corresponding yStart position

PDAnnotationTextMarkup missing when rendering a pdf page to image PDFBox Java

I have code written using PDFBox API that highlights the words in a PDF but when I convert highlighted PDF pages to images, then whatever I have highlighted gets disappeared from the image.
Below screenshot is with highlighted text, for highlighting I have used PDFBox's PDAnnotationTextMarkup class:
Highlighted PDF Page
Below is the image after converting the pdf page to image:
Highlighted PDF Page Image after converting
Below is the code I have used for converting PDF to Image:
PDDocument document = PDDocument.load(new File(pdfFilename));
PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCounter = 0;
for (PDPage page : document.getPages())
{
BufferedImage bim = pdfRenderer.renderImageWithDPI(pageCounter, 300, ImageType.RGB);
ImageIOUtil.writeImage(bim, pdfFilename + "-" + (pageCounter++) + ".png", 300);
}
document.close();
Please suggest what is wrong here, why PDFRenderer not able to take PDF page image along with the highlighted red box.
Below is the code I used to highlight the text in PDF:
private void highlightText(String pdfFilePath, String highlightedPdfFilePath) {
try {
// Loading an existing document
File file = new File(highlightedPdfFilePath);
if (!file.exists()) {
file = new File(pdfFilePath);
}
PDDocument document = PDDocument.load(file);
// extended PDFTextStripper class
PDFTextStripper stripper = new PDFTextHighlighter();
// Get number of pages
int number_of_pages = document.getDocumentCatalog().getPages().getCount();
// The method writeText will invoke an override version of
// writeString
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
stripper.writeText(document, dummy);
// Print collected information
System.out.println("tokenStream:::"+tokenStream);
System.out.println("tokenStream size::"+tokenStream.size());
System.out.println("coordinates size::"+coordinates.size());
double page_height;
double page_width;
double width, height, minx, maxx, miny, maxy;
int rotation;
// scan each page and highlitht all the words inside them
for (int page_index = 0; page_index < number_of_pages; page_index++) {
// get current page
PDPage page = document.getPage(page_index);
// Get annotations for the selected page
List<PDAnnotation> annotations = page.getAnnotations();
// Define a color to use for highlighting text
PDColor red = new PDColor(new float[] { 1, 0, 0 }, PDDeviceRGB.INSTANCE);
// Page height and width
page_height = page.getMediaBox().getHeight();
page_width = page.getMediaBox().getWidth();
// Scan collected coordinates
for (int i = 0; i < coordinates.size(); i++) {
if (!differencePgaeNumber.contains(page_index)) {
differencePgaeNumber.add(page_index);
}
// if the current coordinates are not related to the current
// page, ignore them
if ((int) coordinates.get(i)[4] != (page_index + 1))
continue;
else {
// get rotation of the page...portrait..landscape..
rotation = (int) coordinates.get(i)[7];
// page rotated of 90degrees
if (rotation == 90) {
height = coordinates.get(i)[5];
width = coordinates.get(i)[6];
width = (page_height * width) / page_width;
// define coordinates of a rectangle
maxx = coordinates.get(i)[1];
minx = coordinates.get(i)[1] - height;
miny = coordinates.get(i)[0];
maxy = coordinates.get(i)[0] + width;
} else // i should add here the cases -90/-180 degrees
{
height = coordinates.get(i)[5];
minx = coordinates.get(i)[0];
maxx = coordinates.get(i)[2];
miny = page_height - coordinates.get(i)[1];
maxy = page_height - coordinates.get(i)[3] + height;
}
// Add an annotation for each scanned word
PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(
PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
txtMark.setColor(red);
txtMark.setConstantOpacity((float) 0.3); // 30%
// transparent
PDRectangle position = new PDRectangle();
position.setLowerLeftX((float) minx);
position.setLowerLeftY((float) miny);
position.setUpperRightX((float) maxx);
position.setUpperRightY((float) ((float) maxy + height));
txtMark.setRectangle(position);
float[] quads = new float[8];
quads[0] = position.getLowerLeftX(); // x1
quads[1] = position.getUpperRightY() - 2; // y1
quads[2] = position.getUpperRightX(); // x2
quads[3] = quads[1]; // y2
quads[4] = quads[0]; // x3
quads[5] = position.getLowerLeftY() - 2; // y3
quads[6] = quads[2]; // x4
quads[7] = quads[5]; // y5
txtMark.setQuadPoints(quads);
txtMark.setContents(tokenStream.get(i).toString());
annotations.add(txtMark);
}
}
}
// Saving the document in a new file
File highlighted_doc = new File(highlightedPdfFilePath);
document.save(highlighted_doc);
document.close();
} catch (IOException e) {
System.out.println(e);
}
}
You need to construct the visual appearance of the annotation with this call:
txtMark.constructAppearances(document);

Apache POI - Keep original picture scale while fitting within a merged region

I need to fit a picture into the center of a merged cell region. The merged cell region is always only one column wide, but multiple rows high.
If the picture does not fit inside the cell region, it needs to resize, while keeping the original proportions.
int pictureIdx = wb.addPicture(bytes, pictype);
CreationHelper helper = wb.getCreationHelper();
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
anchor.setCol1(column);
anchor.setRow1(row);
anchor.setRow2(row+rowspan);
anchor.setCol2(column+1);
Picture pict = drawing.createPicture(anchor, pictureIdx);
pict.resize();
short rowHeight = (short)0;
for (int i = row; i<row+rowspan; i++ ) {
rowHeight += sheet.getRow(i).getHeight();
}
int rowHeightPx = heightUnits2Pixel(rowHeight); // (from [https://stackoverflow.com/a/31837639/1320704][1] )
int columnWidthPx = (int) (sheet.getColumnWidthInPixels(column));
int pictWidthPx = pict.getImageDimension().width;
int pictHeightPx = pict.getImageDimension().height;
float scale = 1;
if (pictHeightPx > rowHeightPx) {
float tmpscale = (float)rowHeightPx / (float)pictHeightPx;
if (tmpscale < scale) scale = tmpscale;
}
if (pictWidthPx > columnWidthPx) {
float tmpscale = (float)columnWidthPx / (float)pictWidthPx;
if (tmpscale < scale) scale = tmpscale;
}
anchor.setDx1(
(int) ((sheet.getColumnWidthInPixels(column)- pict.getImageDimension().width) /2)
);
pict.resize(scale, scale);
Here is the result:
As you can see I did not get the desired effect. This picture is a square (256x256) but somehow it got distorted even though scale is passed the same for x and y. Also the height does not match. And it is not centered.
For comparison, here is the same picture in the original size:
And here is what I actually want to achieve:
First problem is that if you wants using Picture.resize, then create an anchor with upper left cell Col1 and Row1 only. Only a one cell anchor since bottom right anchor position depends on resizing.
Second problem is that you should do the resizing only after upper left anchor position is well known inclusive Dx1 and Dy1.
But main problem is that your heightUnits2Pixel does not get the correct pixel heights of the rows. There is Row.getHeightInPoints which should better be used. This gets the row height in pt but this can be converted to px using rowHeightPt * Units.PIXEL_DPI / Units.POINT_DPI. See https://poi.apache.org/apidocs/dev/org/apache/poi/util/Units.html.
Using that at least the scale factor gets calculated correct because it now has the correct row heights in pixel.
To set the Dx1 of the anchor being the horizontal center position there is an additional problem. The meanings of Dx and Dy are very different between XSSF and HSSF.
In XSSF it is the horizontal center position in unit EMU. So you need calculating horCenterPosPx * Units.EMU_PER_PIXEL for Dx. See https://poi.apache.org/apidocs/dev/org/apache/poi/util/Units.html.
In HSSF the values of Dx are dependent on the factor of column-width / default column-width and the values of Dy are dependent on the factor of row-height / default row-height. See apache poi XSSFClientAnchor not positioning picture with respect to dx1, dy1, dx2, dy2.
For calculating the vertical center position at first the correct Row1 of the anchor neeeds to be determined. Even if merged, there are different rows present. So Row1 of the anchor needs to be the correct row where the picture shall start. The remaining pixels up to the vertical center position are the Dy1 then.
Following complete example works for me. It puts any kind of images of different size scaled and centered into the merged range A3:A12.
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class CenterImageTest {
static void putPictureCentered(Sheet sheet, String picturePath, int pictureType, int column, int startRow, int rowspan) throws Exception {
Workbook wb = sheet.getWorkbook();
//load the picture
InputStream inputStream = new FileInputStream(picturePath);
byte[] bytes = IOUtils.toByteArray(inputStream);
int pictureIdx = wb.addPicture(bytes, pictureType);
inputStream.close();
//create an anchor with upper left cell column/startRow, only one cell anchor since bottom right depends on resizing
CreationHelper helper = wb.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(column);
anchor.setRow1(startRow);
//create a picture anchored to Col1 and Row1
Drawing drawing = sheet.createDrawingPatriarch();
Picture pict = drawing.createPicture(anchor, pictureIdx);
//get the picture width in px
int pictWidthPx = pict.getImageDimension().width;
//get the picture height in px
int pictHeightPx = pict.getImageDimension().height;
//get column width of column in px
float columnWidthPx = sheet.getColumnWidthInPixels(column);
//get the heights of all merged rows in px
float[] rowHeightsPx = new float[startRow+rowspan];
float rowsHeightPx = 0f;
for (int r = startRow; r < startRow+rowspan; r++) {
Row row = sheet.getRow(r);
float rowHeightPt = row.getHeightInPoints();
rowHeightsPx[r-startRow] = rowHeightPt * Units.PIXEL_DPI / Units.POINT_DPI;
rowsHeightPx += rowHeightsPx[r-startRow];
}
//calculate scale
float scale = 1;
if (pictHeightPx > rowsHeightPx) {
float tmpscale = rowsHeightPx / (float)pictHeightPx;
if (tmpscale < scale) scale = tmpscale;
}
if (pictWidthPx > columnWidthPx) {
float tmpscale = columnWidthPx / (float)pictWidthPx;
if (tmpscale < scale) scale = tmpscale;
}
//calculate the horizontal center position
int horCenterPosPx = Math.round(columnWidthPx/2f - pictWidthPx*scale/2f);
//set the horizontal center position as Dx1 of anchor
if (wb instanceof XSSFWorkbook) {
anchor.setDx1(horCenterPosPx * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
int DEFAULT_COL_WIDTH = 10 * 256;
anchor.setDx1(Math.round(horCenterPosPx * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75f * DEFAULT_COL_WIDTH / columnWidthPx));
}
//calculate the vertical center position
int vertCenterPosPx = Math.round(rowsHeightPx/2f - pictHeightPx*scale/2f);
//get Row1
Integer row1 = null;
rowsHeightPx = 0f;
for (int r = 0; r < rowHeightsPx.length; r++) {
float rowHeightPx = rowHeightsPx[r];
if (rowsHeightPx + rowHeightPx > vertCenterPosPx) {
row1 = r + startRow;
break;
}
rowsHeightPx += rowHeightPx;
}
//set the vertical center position as Row1 plus Dy1 of anchor
if (row1 != null) {
anchor.setRow1(row1);
if (wb instanceof XSSFWorkbook) {
anchor.setDy1(Math.round(vertCenterPosPx - rowsHeightPx) * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
float DEFAULT_ROW_HEIGHT = 12.75f;
anchor.setDy1(Math.round((vertCenterPosPx - rowsHeightPx) * Units.PIXEL_DPI / Units.POINT_DPI * 14.75f * DEFAULT_ROW_HEIGHT / rowHeightsPx[row1]));
}
}
//resize the picture to it's native size
pict.resize();
//if it must scaled down, then scale
if (scale < 1) {
pict.resize(scale);
}
}
public static void main(String[] args) throws Exception {
Workbook wb = new HSSFWorkbook(); String resultName = "CenterImageTest.xls";
//Workbook wb = new XSSFWorkbook(); String resultName = "CenterImageTest.xlsx";
Sheet sheet = wb.createSheet("Sheet1");
int column = 2;
int columnWidth = 100; //in default character widths
int startRow = 2;
int rowspan = 10;
//========================prepare sheet
//create cell A1 and set cell value
Row row = sheet.createRow(1);
Cell cell = row.createCell(column);
cell.setCellValue("Picture of product");
//create the cells with different heights
for (int r = startRow; r < startRow+rowspan; r++) {
row = sheet.createRow(r);
row.setHeightInPoints(12 + r*2);
}
//merge cells
sheet.addMergedRegion(new CellRangeAddress(startRow,startRow+rowspan-1,column,column));
//========================end prepare sheet
//set column width of column in default character widths
sheet.setColumnWidth(column, columnWidth * 256);
//put image centered
//String picturePath = "./logo.png"; // small image
//putPictureCentered(sheet, picturePath, Workbook.PICTURE_TYPE_PNG, column, startRow, rowspan);
String picturePath = "./Bilder/Sample-jpg-image-1.jpg"; // bigger image
putPictureCentered(sheet, picturePath, Workbook.PICTURE_TYPE_JPEG, column, startRow, rowspan);
FileOutputStream fileOut = new FileOutputStream(resultName);
wb.write(fileOut);
fileOut.close();
wb.close();
}
}
There seems to be a bug in Picture.resize if the picture is small and Dx1is set for the anchor. The scale factor scale gets correctly calculated from this code but pict.resize(scale) does not scale height and width proportionally. It scales the height using the correct factor. But it scales the width not using the same factor but a much bigger factor.
I have filed bug https://bz.apache.org/bugzilla/show_bug.cgi?id=64213.
To avoid the buggy Picture.resize we could calculating the full two cells anchor. We have Col1 and already are calculating Dx1 and Row1 and Dy1 to have the top left point of the picture. Now we could additional calculating Dx2 and Row2and Dy2 to have the bottom right point of the picture too.
Example im my putPictureCentered from above:
...
/*
//resize the picture to it's native size
pict.resize();
//if it must scaled down, then scale
if (scale < 1) {
pict.resize(scale);
}
*/
//set Col2 of anchor the same as Col1 as all is in one column
anchor.setCol2(column);
//calculate the horizontal end position of picture
int horCenterEndPosPx = Math.round(horCenterPosPx + pictWidthPx*scale);
//set set the horizontal end position as Dx2 of anchor
if (wb instanceof XSSFWorkbook) {
anchor.setDx2(horCenterEndPosPx * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
int DEFAULT_COL_WIDTH = 10 * 256;
anchor.setDx2(Math.round(horCenterEndPosPx * Units.DEFAULT_CHARACTER_WIDTH / 256f * 14.75f * DEFAULT_COL_WIDTH / columnWidthPx));
}
//calculate the vertical end position of picture
int vertCenterEndPosPx = Math.round(vertCenterPosPx + pictHeightPx*scale);
//get Row2
Integer row2 = null;
rowsHeightPx = 0f;
for (int r = 0; r < rowHeightsPx.length; r++) {
float rowHeightPx = rowHeightsPx[r];
if (rowsHeightPx + rowHeightPx > vertCenterEndPosPx) {
row2 = r + startRow;
break;
}
rowsHeightPx += rowHeightPx;
}
//set the vertical end position as Row2 plus Dy2 of anchor
if (row2 != null) {
anchor.setRow2(row2);
if (wb instanceof XSSFWorkbook) {
anchor.setDy2(Math.round(vertCenterEndPosPx - rowsHeightPx) * Units.EMU_PER_PIXEL); //in unit EMU for XSSF
} else if (wb instanceof HSSFWorkbook) {
//see https://stackoverflow.com/questions/48567203/apache-poi-xssfclientanchor-not-positioning-picture-with-respect-to-dx1-dy1-dx/48607117#48607117 for HSSF
float DEFAULT_ROW_HEIGHT = 12.75f;
anchor.setDy2(Math.round((vertCenterEndPosPx - rowsHeightPx) * Units.PIXEL_DPI / Units.POINT_DPI * 14.75f * DEFAULT_ROW_HEIGHT / rowHeightsPx[row1]));
}
}
...

Right Alignment of numerical value in PDF using Apache PDFBox library

My Spring boot Java application is using apache pdf box library {version 2.0.6} for generating pdf. I want decimal values to be right aligned. It means all decimal dot should align in same vertical line. I also attached screenShot.
stream.beginText();
stream.newLineAtOffset(xCordinate, yCordinate);
stream.showText(String.valueOf(item.getQuantity()));
List<String> resultList = processTextData(TextUtil.isEmpty(item.getDescription()) ? "-" : item.getDescription());
int y = 0;
int x = 50;
int tempYcordinate = yCordinate;
for (String string : resultList) {
stream.newLineAtOffset(x, y);
stream.showText(processStringForPdf(string));
x = 0;
y = -8;
}
tempYcordinate = tempYcordinate - (8 * resultList.size());
stream.endText();
stream.beginText();
stream.newLineAtOffset(285, yCordinate);
stream.showText("$" + NumberFormat.getInstance(Locale.US).format(Util.round(item.getUnitPrice())));
stream.newLineAtOffset(65, 0);
stream.showText("$" + NumberFormat.getInstance(Locale.US).format(Util.round(item.getExtPrice())));
stream.endText();
yCordinate = tempYcordinate;
To right align the text you need to compute the width of the text to show and align the output position to
(right alignment position) - (text width)
Find below a small snippet which shows the principle. You need to amend the snippet for your needs.
import java.io.File;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
public class RightAlignDemo {
public static void main(String[] args) throws IOException {
File file = new File("out.pdf");
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
PDPageContentStream stream = new PDPageContentStream(doc, page);
PDType1Font font = PDType1Font.TIMES_ROMAN;
int fontSize = 12;
stream.setFont(font, fontSize);
double[] values = {0, 0.1, 0.01, 12.12, 123.12, 1234.12, 123456.12};
int columnOneLeftX = 50;
int columnTwoRightX = 170;
int columnThreeOffsetX = 10;
for (int i = 0; i < values.length; i++) {
stream.beginText();
stream.newLineAtOffset(columnOneLeftX, 700 - (i*10));
// show some left aligned non fixed width text
stream.showText("value " + values[i]);
// format the double value with thousands separator and
// two decimals
String text = String.format("%,.2f", values[i]);
// get the width of the formated value
float textWidth = getTextWidth(font, fontSize, text);
// align the position to (right alignment minus text width)
stream.newLineAtOffset(columnTwoRightX - textWidth, 0);
stream.showText(text);
// align the positon back to columnTwoRightX plus offset for
// column three
stream.newLineAtOffset(textWidth + columnThreeOffsetX, 0);
stream.showText("description " + i);
stream.endText();
}
stream.close();
doc.save(file);
doc.close();
}
private static float getTextWidth(PDType1Font font, int fontSize,
String text) throws IOException {
return (font.getStringWidth(text) / 1000.0f) * fontSize;
}
}
PDF output

Apache PDFBox Java library - Is there an API for creating tables?

I am using the Apache PDFBox java library to create PDFs. Is there a way to create a data-table using pdfbox? If there is no such API to do it, I would require to manually draw the table using drawLine etc., Any suggestions on how to go about this?
Source: Creating tables with PDFBox
The following method draws a table with the specified table content. Its a bit of a hack and will work for small strings of text. It does not perform word wrapping, but you can get an idea of how it is done. Give it a go!
/**
* #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() - margin - 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;
}
}
Usage:
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" );
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.
Since I had the same problem some time ago I started to build a small library for it which I am also trying to keep up to date.
It uses Apache PDFBox 2.x and can be found here:
https://github.com/vandeseer/easytable
It allows for quite some customizations like setting the font, background color, padding etc. on the cell level, vertical and horizontal alignment, cell spanning, word wrapping and images in cells.
Drawing tables across several pages is also possible.
You can create tables like this for instance:
The code for this example can be found here – other examples in the same folder as well.
The accepted answer is nice but it will work with Apache PDFBox 1.x only, for Apache PDFBox 2.x you will need to modify a little bit the code to make it work properly.
So here is the same code but that is compatible with Apache PDFBox 2.x:
The method drawTable:
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 = 20.0f;
final float tableWidth = page.getMediaBox().getWidth() - 2.0f * margin;
final float tableHeight = rowHeight * (float) rows;
final float colWidth = tableWidth / (float) cols;
//draw the rows
float nexty = y ;
for (int i = 0; i <= rows; i++) {
contentStream.moveTo(margin, nexty);
contentStream.lineTo(margin + tableWidth, nexty);
contentStream.stroke();
nexty-= rowHeight;
}
//draw the columns
float nextx = margin;
for (int i = 0; i <= cols; i++) {
contentStream.moveTo(nextx, y);
contentStream.lineTo(nextx, y - tableHeight);
contentStream.stroke();
nextx += colWidth;
}
//now add the text
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12.0f);
final float cellMargin = 5.0f;
float textx = margin + cellMargin;
float texty = y - 15.0f;
for (final String[] aContent : content) {
for (String text : aContent) {
contentStream.beginText();
contentStream.newLineAtOffset(textx, texty);
contentStream.showText(text);
contentStream.endText();
textx += colWidth;
}
texty -= rowHeight;
textx = margin + cellMargin;
}
}
The Usage updated to use the try-with-resources statement to close the resources properly:
try (PDDocument doc = new PDDocument()) {
PDPage page = new PDPage();
doc.addPage(page);
try (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.0f, 100.0f, content);
}
doc.save("test.pdf");
}

Categories

Resources