How to export n number columns for PDF in Java? - java

I have the requirement to generate PDF file with 120 columns(all columns should be on a single page).I am using iText. When I generate pdf columns gets overlapped.
My Questions are:
1)Is there any way to put horizontal scroll bar to navigate between the columns ?
2)How do we prevent overlapping of columns?
I have seen the similar question in this site
(How to export n number columns as headings for PDF in Java?)
but the link mentioned in the answer there
(http://itext-general.2136553.n4.nabble.com/What-is-the-maximum-page-size-for-PDF-in-iText-td2150839.html%22) is inactive.
Please suggest me on this.

Following code will set the page size. So, If we have large number of columns (for example: 120 as in my case), it will automatically show horizontal bar in pdf to navigate.
Rectangle pageSize=new Rectangle(9400f,9400f);
Document doc=new Document(pageSize);
PdfWriter writer=PdfWriter.getInstance(document,baos);
writer.setUserunit(9900f);
// To prevent overlapping of columns, set the column widths as follows
PdfPTable table=new PdfPTable(120); // 120 is number of columns in table
table.setWidths(600f);

I had the similar problem but thanks to this question answer series i got the solution as stated by Mr.Harjinder, but here's a tip for those who face the same problem create the document after you create the PDF elements and keep the track of the width you want and the finally when you have the track of horizontal scrolling create your document below is the example
a PDF with lists and with horizontal scroll bar
public class Sample {
public static void main(String[] args) {
try {
float indentationLeft = 20f;
float indentationLeftTemp = 20f;
List prtLst = new List(List.UNORDERED);
prtLst.setListSymbol("\u2022");
prtLst.add("P1");
prtLst.setListSymbol("+");
prtLst.add("P2");
prtLst.setListSymbol("\u2022");
List prevList= prtLst;
for(int i=0;i<500;i++){
List chldLst2 = new List(List.UNORDERED);
chldLst2.setIndentationLeft(indentationLeft);
indentationLeftTemp+=20f;
chldLst2.setListSymbol("\u2022");
chldLst2.add("YY"+i);
prevList.add(chldLst2);
prevList=chldLst2;
}
prtLst.add("P3");
Document d = null;
if(indentationLeftTemp>400f){
Rectangle pageSize=new Rectangle(indentationLeftTemp+200f,indentationLeftTemp+200f);
d = new Document(pageSize);
}else{
d = new Document();
}
PdfWriter.getInstance(d, new FileOutputStream("D:/test.pdf"));
d.open();
d.add(prtLst);d.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Related

Java iText7 get Table height [duplicate]

In iText5, we can get the PdfPTable's height when we need "public float calculateHeights(boolean firsttime)".
But in iText7, how can we get current table height value (especially before adding the table to its parent element)?
I already tested "table.getHeight()" method, but it returns null.
And I also found that in a table render object I can get this value, but the limitation is that the render need to be triggered when the table is adding into its parent element, so the time is not my need.
Cause sometimes we need this value for calculation to decide the "y-axis" value.
In iText5, elements and information about their position/size were a bit mixed together, which allowed you to call calculateWidths on a PdfPTable element.
In iText7, this functionality is separated, which allows different kind of flexibility for rendering/layouting elements.
Thus, model elements, which a Table instance is an example of, do not know anything about their position or size. And calling table.getHeight results in null because table did not have HEIGHT property previously set to it.
To calculate table height, one would have to make use of the rendering functionality.
For a model element, you can get the subtree of renderers representing this model element and all its children, and layout it in any given area. To really know the height of a table, you would want to create an area which knowingly will be sufficient to place the whole contents of the element.
PdfDocument pdfDoc = ...
Document doc = ...
Table table = new Table(2)
.addCell(new Cell().add(new Paragraph("cell 1, 1")))
.addCell(new Cell().add(new Paragraph("cell 1, 2")));
LayoutResult result = table.createRendererSubTree().setParent(doc.getRenderer()).layout(
new LayoutContext(new LayoutArea(1, new Rectangle(0, 0, 400, 1e4f))));
System.out.println(result.getOccupiedArea().getBBox().getHeight());
The code above prints 22.982422O for me, but the results may vary depending on the configuration and properties of elements.
I would like to point out two important parts of the code:
We pass 1e4f as the height of the LayoutArea, considering that this will be sufficient to place the whole table. Note that if the table cannot be placed into that height, the result will never exceed this given height and thus it will not be correct for your usecase (know the total height of the table). So make sure to pass the height which will be sufficient for placement of the whole table.
.setParent(doc.getRenderer()) part is important here and is used for retrieving inheritant properties. Note that we did not set a lot of properties to table element, even font, but this information is essential to know the area this element would occupy. So this information will be inherited from the parent chain during layout. You can test this by changing the document's font: document.setFont(newFont);, or font size: document.setFontSize(24); and watching the resultant height change
Well, due to the way the renderer framework is written in iText7, there isn't a way (yet) to calculate the height of a layout object before it is added to a parent document, since the actual calculation of the height for the layout objects happens when they are added to the a Document object.
You can however relayout a Document, allowing you to change the content of previously added elements. Using this, you can simulate the rendering of tables and get a hold of their heights when you add new elements. The table.getHeight() still won't work, since it retrieves the height property, and that property is currently not set anywhere in the table rendering process.
In the example below, I've written a convenience method that iterates over the renderer-tree and prints out the area each table occupies in the document, to show you how you can get the calculated heights.
The example itself adds some tables to the document, displays the occupied areas, adds some cells to each table, displays the occupied areas (they're the same since adding to an element that has been added before doesn't trigger a layout), and finally, manually triggers a relayout and displays the final occupied areas.
public class DelayedLayout {
public static String DEST = "target/output/StackOverflow/DelayedLayout/delayed.pdf";
public static void main(String[] args)throws IOException, FileNotFoundException{
File file = new File(DEST);
file.getParentFile().mkdirs();
new DelayedLayout().createPdf(DEST);
}
public void createPdf(String dest) throws IOException, FileNotFoundException{
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(writer);
boolean immediateFlush = false;
boolean relayout = true;
//Set immediate layout to false, so the document doesn't immediatly write render-results to its outputstream
Document doc = new Document(pdfDoc, PageSize.A4,immediateFlush);
Table tOne = createSimpleTable();
for(int i= 0; i< 5; i++) {
//Add a table and some whitespace
doc.add(tOne);
doc.add(new Paragraph(""));
}
System.out.println("\nInitial layout results");
printOccupiedAreasOfTableRenderers(doc.getRenderer());
System.out.println("\nAdding extra cells to the table");
addToTable(tOne);
printOccupiedAreasOfTableRenderers(doc.getRenderer());
System.out.println("\nForcing the document to redo the layout");
if(relayout)doc.relayout();
printOccupiedAreasOfTableRenderers(doc.getRenderer());
doc.close();
}
/**
* Create a very simple table
* #return simple table
*/
private Table createSimpleTable(){
int nrOfCols = 3;
int nrOfRows = 5;
Table res = new Table(nrOfCols);
for(int i= 0; i<nrOfRows;i++){
for(int j = 0; j<nrOfCols;j++){
Cell c = new Cell();
c.add(new Paragraph("["+i+", "+j+"]"));
res.addCell(c);
}
}
return res;
}
/**
* Add some extra cells to an exisiting table
* #param tab table to add cells to
*/
private void addToTable(Table tab){
int nrOfRows = 5;
int nrOfCols = tab.getNumberOfColumns();
for(int i=0; i<nrOfRows*nrOfCols;i++){
Cell c = new Cell();
c.add(new Paragraph("Extra cell"+ i));
tab.addCell(c);
}
}
/**
* Recursively iterate over the renderer tree, writing the occupied area to the console
* #param currentNode current renderer-node to check
*/
private void printOccupiedAreasOfTableRenderers(IRenderer currentNode){
if(currentNode.getClass().equals(TableRenderer.class)){
System.out.println("Table renderer with occupied area: " + currentNode.getOccupiedArea());
}
for (IRenderer child:currentNode.getChildRenderers()) {
printOccupiedAreasOfTableRenderers(child);
}
}

How can I fill the remaining blank portion of the page in Itext5?

As seen above,i used pdfTable as the body part of the page. Now i want to fill the entire body with the border,column,row of the table, if the table does not fill the entire body part.As shown in the picture below.
Thanks very much!
You can sort of cheat to accomplish this if you do it while you are creating the table.
I'll offer a partial solution that takes advantage of one of the complexities tables in PDFs: Tables are just lines in PDFs. They aren't structured content.
You can take advantage of this though- keep track of where you are are drawing vertical lines while rendering the table and simply continue them to the bottom of the page.
Let's create a new cell event. It keeps track of 4 things: left which is the far left x coordinate of the table, right which is the far right x coordinate of the table, xCoordinates which is a set of all the x coordinates we draw vertical lines, and finally cellHeights which is a list off all the cell heights.
class CellMarginEvent implements PdfPCellEvent {
Set<Float> xCoordinates = new HashSet<Float>();
Set<Float> cellHeights = new HashSet<Float>();
Float left = Float.MAX_VALUE;
Float right = Float.MIN_VALUE;
public void cellLayout(PdfPCell pdfPCell, Rectangle rectangle, PdfContentByte[] pdfContentBytes) {
this.xCoordinates.add(rectangle.getLeft());
this.xCoordinates.add(rectangle.getRight());
this.cellHeights.add(rectangle.getHeight());
left = Math.min(left,rectangle.getLeft());
right = Math.max(right, rectangle.getRight());
}
public Set<Float> getxCoordinates() {
return xCoordinates;
}
}
We'll then add all of our cells to the table, but not add the table to the document just yet
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(OUTPUT_FILE));
document.open();
PdfPTable table = new PdfPTable(4);
CellMarginEvent cellMarginEvent = new CellMarginEvent();
for (int aw = 0; aw < 320; aw++) {
PdfPCell cell = new PdfPCell();
cell.addElement(new Paragraph("Cell: " + aw));
cell.setCellEvent(cellMarginEvent);
table.addCell(cell);
}
No we add get top- the top position of our table, and add the table to the document.
float top = writer.getVerticalPosition(false);
document.add(table);
Then we draw the vertical and horizontal lines of the completed table. For the height of each cell I just used the first element in cellHeights.
Set<Float> xCoordinates = cellMarginEvent.getxCoordinates();
//Draw the column lines
PdfContentByte canvas = writer.getDirectContent();
for (Float x : xCoordinates) {
canvas.moveTo(x, top);
canvas.lineTo(x, 0 + document.bottomMargin());
canvas.closePathStroke();
}
Set<Float> cellHeights = cellMarginEvent.cellHeights;
Float cellHeight = (Float)cellHeights.toArray()[0];
float currentPosition = writer.getVerticalPosition(false);
//Draw the row lines
while (currentPosition >= document.bottomMargin()) {
canvas.moveTo(cellMarginEvent.left,currentPosition);
canvas.lineTo(cellMarginEvent.right,currentPosition);
canvas.closePathStroke();
currentPosition -= cellHeight;
}
And finally close the document:
document.close()
Example output:
Note that the only reason I say this is an incomplete examples is because there may be some adjustments you need to make to top in the case of header cells, or there may be custom cell styling (background color, line color, etc) you need to account for yourself.
I'll also note another downfall that I just thought of- in the case of tagged PDFs this solution fails to add tagged table cells, and thus would break compliance if you have that requirement.

iText shrink table column to fit contents

In my iText document, I have a lot of tables scattered around, each with only one row of two columns. I would like to automatically shrink the leftmost column to fit its contents, and expand the rightmost column to fill the remaining space.
The exact contents of these two columns varies greatly, so there's no way to determine ahead of time what the exact width should be.
All of the content in this screenshot is wrapped in one outer table. Each nested table has its two columns highlighted red and blue. I would like to shrink the red columns as narrow as they can get without forcing the text to take up more lines than it has to.
In this case, the contents of the red cells are just a paragraph each, but it's possible they may contain a further-nested table with two cells of its own (which probably faces the same problem).
Is there a simple way to expand one column and shrink another without specifying exact or relative widths?
If you're using iText7 (and ditching the table for layout altogether), you can achieve this look and layout by building on the following example:
Output looks like this:
Code used to generate output above:
public void createPdf(String dest) throws IOException, FileNotFoundException{
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(writer);
Document doc = new Document(pdfDoc);
Paragraph p = new Paragraph();
Text t = new Text("Date:").setBold();
p.add(t);
t= new Text("10/12/17").setUnderline();
p.add(t);
p.add(new Tab());
p.add(createTwoPartBorderedText("Catalog Year: ","2017"));
p.add(new Tab());
p.add(createTwoPartBorderedText("L Number","2019284"));
doc.add(p);
doc.close();
}
public Paragraph createTwoPartBorderedText(String contentOne, String contentTwo){
Paragraph container= new Paragraph();
Text one = new Text(contentOne).setBold();
Border solidRed = new SolidBorder(Color.RED,1f);
one.setBorder(solidRed);
container.add(one);
Text two =new Text(contentTwo);
two.setUnderline();
Border solidBlue = new SolidBorder(Color.BLUE,1f);
two.setBorder(solidBlue);
container.add(two);
return container;
}

Adding footer to existing PDF

I am trying to add footer to my existing PDF. I did add one footer to the PDF.
Is there anyway to add 2 lines of footer? This is my code below:
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(new File("D:/TestDestination/Merge Output1.pdf")));
document.open();
PdfReader reader1 = new PdfReader("D:/TestDestination/Merge Output.pdf");
int n1 = reader1.getNumberOfPages();
PdfImportedPage page;
PdfCopy.PageStamp stamp;
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
for (int i = 0; i < n1; ) {
page = copy.getImportedPage(reader1, ++i);
stamp = copy.createPageStamp(page);
ColumnText.showTextAligned(stamp.getUnderContent(), Element.ALIGN_CENTER,new Phrase(String.format("page %d of %d", i, n1)),297.5f, 28, 0);
stamp.alterContents();
copy.addPage(page);
}
document.close();
reader1.close();
Please go to the official documentation and click Q&A to go to the Frequently Asked Questions. Select Absolute positioning of text.
You are currently using ColumnText in a way that allows you to add a single line of text. You are using ColumnText.showTextAligned(...) as explained in my answer to the question How to rotate a single line of text?
You should read the answers to questions such as:
How to add text at an absolute position on the top of the first page?
How to add text inside a rectangle?
How to truncate text within a bounding box?
How to fit a String inside a rectangle?
How to reduce redundant code when adding content at absolute positions?
Assuming that you don't have access to the official web site (otherwise you wouldn't have posted your question), I'm adding a short code snippet:
ColumnText ct = new ColumnText(stamp.getUnderContent());
ct.setSimpleColumn(rectangle);
ct.addElement(new Paragraph("Whatever text needs to fit inside the rectangle"));
ct.go();
In this snippet, stamp is the object you created in your code. The rectangle object is of type Rectangle. Its parameters are the coordinates of the lower-left and upper-right corner of the rectangle in which you want to render the multi-line text.
Caveat: all text that doesn't fit the rectangle will be dropped. You can avoid this by adding the text in simulation mode first. If the text fits, add it for real. If it doesn't fit, try anew using a smaller font or a bigger rectangle.

Keep cell content on one page

I have a table, with one column. Each cell contains a paragraph.
How can I stop paragraphs from splitting across two pages?
PdfPtable table = new PdfPTable(1);
//report must be printed as compat as possible
table.setSplitLate(false);
//I can't set keep together, because table can be larger than page size
//table.setKeepTogether(true);
for (int i = 0; i < 100; i++) {
//Random text. Can contain ~400 chars.
String text = "aaaaaaaaaaaaaaa sssssssssssss ddddddddddd ffffffffff";
Paragraph p = new Paragraph(text);
//That instruction does not work. I don't know why, may be because paragraph printed in cell.
p.setKeepTogether(true);
table.addCell(p);
}
Change
table.setSplitLate(false);
into
table.setSplitLate(true);
This way, your cell will not be split unless the complete cell doesn't fit on a single page.

Categories