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
Related
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();
}
In my report generation java application with iText 7, I need to get data from a large data tables which may extend to several pages.
My code segments to generate the table.
Table table = new Table(new float[] {0.4f, 1f, 1f, 1f, 1.3f, 1f, 1.3f, 0.6f,0.6f,1.2f}, true)
.setWidth(UnitValue.createPercentValue(100))
.setMarginTop(tblTopMargin)
.setMarginBottom(0);
int count = 0;
while (!dataList.empty()) {
String[] dataRow = dataList.poll();
createDataRow(dataRow, table);
count++;
if(count % 10 == 0) {
table.flush();
}
}
implementation of createDataRaw method is mentioned below,
private void createDataRow(String[] a, Table table) {
for (String s : a) {
Paragraph content = new Paragraph(s)
.setFontSize(7)
.setFixedLeading(9)
.setFontColor(new DeviceCmyk(0, 0, 0, 100));
Cell cell = new Cell()
.setBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
.setPaddingLeft(2)
.setPaddingTop(0)
.setPaddingBottom(0)
.setHorizontalAlignment(HorizontalAlignment.LEFT)
.setVerticalAlignment(VerticalAlignment.MIDDLE)
.add(content);
table.addCell(cell);
}
}
with the given code table generated with all the data. But when there is a page break between tables then the bottom line of the table is not showing except for the last table bottom.
screenshots are attached here to get a more clear idea.
Can someone help me to solve this issue?
The following code produces the desired result for me for the latest 7.1.16 version of iText:
Table table = new Table(new float[] {0.4f, 1f, 1f}, true)
.setWidth(UnitValue.createPercentValue(100))
.setMarginBottom(0);
document.add(table);
int count = 0;
for (int i = 0; i < 300; i++) {
String[] dataRow = new String[] {"1\n2\n3\n4\n5\n6\n7\n8\n9\n10", "2\n3\nsf\n43", "3\nr\nsdfsd\n43"};
createDataRow(dataRow, table);
count++;
if (count % 10 == 0) {
table.flush();
}
}
table.complete();
document.close();
private void createDataRow(String[] a, Table table) {
for (String s : a) {
Paragraph content = new Paragraph(s)
.setFontSize(7)
.setFixedLeading(9)
.setFontColor(new DeviceCmyk(0, 0, 0, 100));
Cell cell = new Cell()
.setBorder(new SolidBorder(ColorConstants.BLACK, 0.5f))
.setPaddingLeft(2)
.setPaddingTop(0)
.setPaddingBottom(0)
.setHorizontalAlignment(HorizontalAlignment.LEFT)
.setVerticalAlignment(VerticalAlignment.MIDDLE)
.add(content);
table.addCell(cell);
}
}
Visual result (end of first page):
I'm creating a word document with a table using Java and Apache POI.
I can create the table easily enough, set each column with different widths and then merge the cells to produce the desired effect (see images below) however when I open the word document some of the cells have been adjusted so that their edges snap together. I have found that adding an additional row to the beginning of the table and leaving all cells unmerged keeps the rest of the rows intact, but removing this row later using table.removeRow(0); affects the rest of the rows. If I open the word document and manually delete the row, the cells stay where they are. Is there anything I can do to preserve the layout of the cells?
correct layout with an additional unmerged top row
the result after removing the top row
This is the function that creates the word doc and table:
public static void createWord() {
// Blank Document
XWPFDocument document = new XWPFDocument();
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageMar pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(300L));
pageMar.setTop(BigInteger.valueOf(300L));
pageMar.setRight(BigInteger.valueOf(300L));
pageMar.setBottom(BigInteger.valueOf(300L));
XWPFParagraph paragraph = document.createParagraph();
paragraph.setSpacingBefore(0);
paragraph.setSpacingAfter(0);
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
// create table
XWPFTable table = document.createTable(rows+1, cols);
CTTblPr tblPr = table.getCTTbl().getTblPr();
if (null == tblPr) {
tblPr = table.getCTTbl().addNewTblPr();
}
// set table width
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.PCT);
width.setW(BigInteger.valueOf(5000)); // 5000 * 1/50 = 100%
//set row height
for(XWPFTableRow row:table.getRows()) {
row.setHeight(22);
}
// set width of each column
for (int row = 0; row <= rows; row++) {
setCellWidthPercentage(table, row, 0, 0.188);
setCellWidthPercentage(table, row, 1, 0.125);
setCellWidthPercentage(table, row, 2, 0.063);
setCellWidthPercentage(table, row, 3, 0.25);
setCellWidthPercentage(table, row, 4, 0.25);
setCellWidthPercentage(table, row, 5, 0.125);
}
mergeCellHorizontally(table, 1, 0, 2);
mergeCellHorizontally(table, 2, 0, 1);
mergeCellHorizontally(table, 2, 2, 4);
mergeCellHorizontally(table, 3, 1, 3);
// remove first row (comment out this line to see issue)
table.removeRow(0);
// Write the Document in file system
try {
File docFile = new File("C:\\doc.docx");
docFile.createNewFile();
FileOutputStream out = new FileOutputStream(docFile, false);
document.write(out);
out.close();
document.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
I'm using the code below to merge cells horizontally:
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
for(int colIndex = fromCol; colIndex <= toCol; colIndex++){
XWPFTableCell cell = table.getRow(row).getCell(colIndex);
CTHMerge hmerge = CTHMerge.Factory.newInstance();
if(colIndex == fromCol) {
// The first merged cell is set with RESTART merge value
hmerge.setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
hmerge.setVal(STMerge.CONTINUE);
}
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setHMerge(hmerge);
} else {
// only set an new TcPr if there is not one already
tcPr = CTTcPr.Factory.newInstance();
tcPr.setHMerge(hmerge);
cell.getCTTc().setTcPr(tcPr);
}
}
}
and this function to assign width values to the columns before merging:
private static void setCellWidthPercentage(XWPFTable table, int row, int col, double width) {
// prevent out of bounds exception
if (row < 0 || row >= table.getRows().size()) return;
if (col < 0 || col >= table.getRow(row).getTableCells().size()) return;
// assign widths in units of 1/50 of a percentage
CTTblWidth tblW = table.getRow(row).getCell(col).getCTTc().addNewTcPr().addNewTcW();
tblW.setType(STTblWidth.PCT);
tblW.setW(BigInteger.valueOf(Math.round(width * 50)));
}
Thanks in advance!
The problem you see is that Word renders tables in respect of the column width settings of the row which has the most columns in it. If other rows contradict the column width settings of that row, then their column width setting will be ignored. And after merging cells you are not correcting the column width settings. For example after mergeCellHorizontally(table, 0, 0, 2); column 0 in row 0 is up to column 2 now. So column 0 now need width of formerly columns 0 + 1 + 2. But since you are not correcting that, it stays width of formerly column 0 only and gets ignored while rendering if that contradicts the width settings of the row having the most columns.
So the main problem is that your code lacks correcting the column width settings in the rows after merging cells.
I have shown this already in how to set specific cell width in different row in apache poi table?.
But there are more issues.
First the method mergeCellHorizontally should merge cells horizontally by setting grid span instead of using CTHMerge. This is much more compatible to all kinds of word processing applications which open *.docx files than using CTHMerge.
Second there always should be used the last apache poi version. Current apache poi 4.1.2 provides XWPFTable.setWidth and XWPFTableCell.setWidth. So no own set-width-methods are necessary.
And third you should create a table grid for the table with widths of the columns. This is necessary for Libreoffice/OpenOffice to accept the column widths. Unfortunately this needs calculating the column widths in unit twentieths of a point (1/1440 of an inch) since TblGrid - GridCol does not accepts percent values.
The following complete example shows all this and creates the table you want.
import java.io.FileOutputStream;
import java.math.BigInteger;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
public class CreateWordTableMergedCells {
//merging horizontally by setting grid span instead of using CTHMerge
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
XWPFTableCell cell = table.getRow(row).getCell(fromCol);
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
// The first merged cell has grid span property set
if (tcPr.isSetGridSpan()) {
tcPr.getGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
} else {
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
}
// Cells which join (merge) the first one, must be removed
for(int colIndex = toCol; colIndex > fromCol; colIndex--) {
table.getRow(row).getCtRow().removeTc(colIndex);
table.getRow(row).removeCell(colIndex);
}
}
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:");
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
//create table
XWPFTable table = document.createTable(rows, cols);
//set table width
table.setWidth("100%");
double[] columnWidths = new double[] { // columnWidths in percent
0.188, 0.125, 0.062, 0.25, 0.25, 0.125
};
//create CTTblGrid for this table with widths of the columns.
//necessary for Libreoffice/Openoffice to accept the column widths.
//values are in unit twentieths of a point (1/1440 of an inch)
int w100Percent = 6*1440; // twentieths of a point (1/1440 of an inch); 6 inches
//first column
table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[0])));
//other columns
for (int c = 1; c < cols; c++) {
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[c])));
}
// set width of each column in each row
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
table.getRow(r).getCell(c).setWidth("" + (columnWidths[c]*100.0) + "%");
}
}
//using the merge method
mergeCellHorizontally(table, 0, 0, 2); // after that column 0 is up to column 2
//column 0 now need width of formerly columns 0 + 1 + 2
table.getRow(0).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1]+columnWidths[2])*100.0) + "%");
mergeCellHorizontally(table, 1, 0, 1); // after that column 0 is up to column 1
//column 0 now need width of formerly columns 0 + 1
table.getRow(1).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1])*100.0) + "%");
mergeCellHorizontally(table, 1, 1, 3); // formerly col 2 is now col 1 and after that formerly column 2 is up to column 4
//current column 1 now need width of formerly columns 2 + 3 + 4
table.getRow(1).getCell(1).setWidth("" + ((columnWidths[2]+columnWidths[3]+columnWidths[4])*100.0) + "%");
mergeCellHorizontally(table, 2, 1, 3); // after that column 1 is up to column 3
//column 1 now need width of formerly columns 1 + 2 + 3
table.getRow(2).getCell(1).setWidth("" + ((columnWidths[1]+columnWidths[2]+columnWidths[3])*100.0) + "%");
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("create_table.docx");
document.write(out);
out.close();
}
}
I have started working with Apache POI to create an Excel sheet that contains a chart. The chart is using data that is pretty simple, a date column and a numerical value column. I want to display the data in date reverse order so the newest data is at the top row in the spreadsheet, but I would like the chart to show the data in date normal order, because I'm also including a trend line, and the direction of the trend line is completely dependent on the ordering of the data. The date is being displayed on the x-axis, with the y-axis containing the numerical values. What I want to do is basically reverse all of the data on the chart. Is there some way to do this? In Google Sheets, it happens automatically, but that is not an option.
Here's the code I'm currently experimenting with:
public void addChart() {
final int NUM_OF_ROWS = ytd.size();
final int NUM_OF_COLUMNS = 2;
// Create a row and put some cells in it. Rows are 0 based.
XSSFRow row;
XSSFCell cell;
List<String> dates = new ArrayList<>();
List<Double> values = new ArrayList<>();
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = (XSSFRow)sheet.createRow((short) rowIndex);
row.createCell(0).setCellValue(ytd.get(rowIndex).getShipDate());
dates.add(ytd.get(rowIndex).getShipDate());
for (int colIndex = 1; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(new BigDecimal(ytd.get(rowIndex).getSquareFeet().replaceAll("[,]", "")).doubleValue());
values.add(new BigDecimal(ytd.get(rowIndex).getSquareFeet().replaceAll("[,]", "")).doubleValue());
}
}
Collections.reverse(dates);
Collections.reverse(values);
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.getRow((short) rowIndex);
row.createCell(2).setCellValue(dates.get(rowIndex));
for (int colIndex = 3; colIndex < 4; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(values.get(rowIndex));
}
}
XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(5, 5, 20, 20, 4, 4, 20, 25);
XSSFChart chart = drawing.createChart(anchor);
CTChart ctChart = ((XSSFChart) chart).getCTChart();
// XSSFChart chart = drawing.createChart(anchor);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
// Use a category axis for the bottom axis.
XDDFDateAxis bottomAxis = chart.createDateAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Ship Date"); // https://stackoverflow.com/questions/32010765
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Square Feet");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<Double> xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 2, 2));
XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, NUM_OF_ROWS-1, 3, 3));
XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(xs, ys1);
series1.setTitle("SqFt", null); // https://stackoverflow.com/questions/21855842
series1.setSmooth(false); // https://stackoverflow.com/questions/29014848
series1.setMarkerStyle(MarkerStyle.DOT); // https://stackoverflow.com/questions/39636138
chart.plot(data);
// sheet.setColumnHidden(2, true);
// sheet.setColumnHidden(3, true);
// if your series have missing values like https://stackoverflow.com/questions/29014848
// chart.displayBlanksAs(DisplayBlanks.GAP);
// https://stackoverflow.com/questions/24676460
solidLineSeries(data, 0, PresetColor.CHARTREUSE);
// solidLineSeries(data, 1, PresetColor.TURQUOISE);
chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0)
.addNewTrendline()
.addNewTrendlineType()
.setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STTrendlineType.LINEAR);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) {
workbook.write(fileOut);
} catch (FileNotFoundException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
I've tried putting the data in columns that are hidden, which works fine in LibreOffice, but doesn't work in excel until I un-hide the columns. So does anyone know if there is a way to control the ordering of the graph data, separate from the data in the table?
I found the solution: you can change the direction of the data by modifying the category axis on the chart, using the enum AxisOrientation. A side effect of getting the data to chart in the order I want, is that the labels for the y-axis now appear on the right instead of the left, but I think I can live with that.
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");
}