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.
Related
I am shrinking pdf using below code. Before shrinking PDF pages can be seen in Portrait, but after shrinking their orientation is changing to Landscape. When I print rotation of page before shrinking it is coming as 270 degree. What is causing page to rotate after shrinking? (The PDF which i am trying to shrink has old scanned images)
public void shrinkPDF(String strFilePath , String strFileName) throws Exception {
PdfReader reader = new PdfReader(strFilePath+"//"+strFileName);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(strFilePath+"//Shrink_"+strFileName));
int n = reader.getNumberOfPages();
for (int p = 1; p <= 1; p++) {
float offsetX = (reader.getPageSize(p).getWidth() * (1 - xPercentage)) / 2;
float offsetY = (reader.getPageSize(p).getHeight() * (1 - yPercentage)) / 2;
PdfDictionary page;
PdfArray crop;
PdfArray media;
page = reader.getPageN(p);
System.out.println("reader.getPateRoatation-->"+reader.getPageRotation(p));
media = page.getAsArray(PdfName.CROPBOX);
if (media == null) {
media = page.getAsArray(PdfName.MEDIABOX);
}
crop = new PdfArray();
crop.add(new PdfNumber(0));
crop.add(new PdfNumber(0));
crop.add(new PdfNumber(media.getAsNumber(2).floatValue()));
crop.add(new PdfNumber(media.getAsNumber(3).floatValue()));
page.put(PdfName.MEDIABOX, crop);
page.put(PdfName.CROPBOX, crop);
Rectangle mediabox = reader.getPageSize(p);
stamper.getUnderContent(p).setLiteral(
String.format("\nq %s %s %s %s %s %s cm\nq\n",
xPercentage, mediabox.getLeft(),mediabox.getBottom(), yPercentage, offsetX, offsetY));
stamper.getOverContent(p).setLiteral("\nQ\nQ\n");
}
stamper.close();
reader.close();
}
The cause
The cause for the issue is a feature of iText:
iText tries to simplify adding information to a rotated page by starting both the overcontent and the undercontent with a rotation of the current transformation matrix. This makes additions to the page appear upright in a PDF viewer without the need to add individual rotations.
Even though the undercontent is drawn before the original page content, this normally has no effect on that original content because the whole undercontent is enveloped in a save-graphics-state / restore-graphics-state instruction pair.
The literal you use as undercontent, though, contains two save-graphics-state instructions and no restore-graphics-state instruction. This makes the added rotation suddenly affect the original content, too. Thus, your original content is rotated even though you only want to scale.
The fix
iText allows you to switch off the feature described above. You can do so by setting the PdfStamper property RotateContents to false right after creating the PdfStamper:
PdfStamper stamper = new PdfStamper(reader, result);
stamper.setRotateContents(false);
int n = reader.getNumberOfPages();
Now iText won't add that rotation anymore to the undercontent and your original only is scaled.
The PdfStamper property RotateContents has been discussed more deeply in this answer.
Annotation considerations
iText does not only add the rotation to undercontent and overcontent of page content streams, it also manipulates the dimensions of annotations added to rotated pages, and unfortunately the PdfStamper property RotateContents is not taken into account for that.
A work-around in that case is to temporarily remove the page Rotation entry before adding the annotation to the page and to put it back again later. This has already been discussed in more detail in this answer, this answer, and this answer.
Your remaining code
Your changes to crop box and media box seem unnecessary and might have unexpected and undesired results.
You add the shrinking like this:
stamper.getUnderContent(p).setLiteral(
String.format("\nq %s %s %s %s %s %s cm\nq\n",
xPercentage, mediabox.getLeft(),mediabox.getBottom(), yPercentage, offsetX, offsetY));
Setting the second and third parameter to mediabox.getLeft() and mediabox.getBottom() respectively often will have no bad effect (as these values often are 0) but in some cases you'll experience extremely distorted views of (enlarged parts of) your page.
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;
}
In my project I use iText to generate a PDF document.
Suppose that the height of a page measures 500pt (1 user unit = 1 point), and that I write some text to the page, followed by an image.
If the content and the image require less than 450pt, the text preceded the image.
If the content and the image exceed 450pt, the text is forwarded to the next page.
My question is: how can I obtain the remaining available space before writing an image?
First things first: when adding text and images to a page, iText sometimes changes the order of the textual content and the image. You can avoid this by using:
writer.setStrictImageSequence(true);
If you want to know the current position of the "cursor", you can use the method getVerticalPosition(). Unfortunately, this method isn't very elegant: it requires a Boolean parameter that will add a newline (if true) or give you the position at the current line (if false).
I do not understand why you want to get the vertical position. Is it because you want to have a caption followed by an image, and you want the caption and the image to be at the same page?
In that case, you could put your text and images inside a table cell and instruct iText not to split rows. In this case, iText will forward both text and image, in the correct order to the next page if the content doesn't fit the current page.
Update:
Based on the extra information added in the comments, it is now clear that the OP wants to add images that are watermarked.
There are two approaches to achieve this, depending on the actual requirement.
Approach 1:
The first approach is explained in the WatermarkedImages1 example. In this example, we create a PdfTemplate to which we add an image as well as some text written on top of that image. We can then wrap this PdfTemplate inside an image and add that image together with its watermark using a single document.add() statement.
This is the method that performs all the magic:
public Image getWatermarkedImage(PdfContentByte cb, Image img, String watermark) throws DocumentException {
float width = img.getScaledWidth();
float height = img.getScaledHeight();
PdfTemplate template = cb.createTemplate(width, height);
template.addImage(img, width, 0, 0, height, 0, 0);
ColumnText.showTextAligned(template, Element.ALIGN_CENTER,
new Phrase(watermark, FONT), width / 2, height / 2, 30);
return Image.getInstance(template);
}
This is how we add the images:
PdfContentByte cb = writer.getDirectContentUnder();
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE1), "Bruno"));
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE2), "Dog"));
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE3), "Fox"));
Image img = Image.getInstance(IMAGE4);
img.scaleToFit(400, 700);
document.add(getWatermarkedImage(cb, img, "Bruno and Ingeborg"));
As you can see, we have one very large image (a picture of my wife and me). We need to scale this image so that it fits the page. If you want to avoid this, take a look at the second approach.
Approach 2:
The second approach is explained in the WatermarkedImages2 example. In this case, we add each image to a PdfPCell. This PdfPCell will scale the image so that it fits the width of the page. To add the watermark, we use a cell event:
class WatermarkedCell implements PdfPCellEvent {
String watermark;
public WatermarkedCell(String watermark) {
this.watermark = watermark;
}
public void cellLayout(PdfPCell cell, Rectangle position,
PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER,
new Phrase(watermark, FONT),
(position.getLeft() + position.getRight()) / 2,
(position.getBottom() + position.getTop()) / 2, 30);
}
}
This cell event can be used like this:
PdfPCell cell;
cell = new PdfPCell(Image.getInstance(IMAGE1), true);
cell.setCellEvent(new WatermarkedCell("Bruno"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE2), true);
cell.setCellEvent(new WatermarkedCell("Dog"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE3), true);
cell.setCellEvent(new WatermarkedCell("Fox"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE4), true);
cell.setCellEvent(new WatermarkedCell("Bruno and Ingeborg"));
table.addCell(cell);
You will use this approach if all images have more or less the same size, and if you don't want to worry about fitting the images on the page.
Consideration:
Obviously, both approaches have a different result because of the design choice that is made. Please compare the resulting PDFs to see the difference: watermark_template.pdf versus watermark_table.pdf
How do I set the position of text so that it is centered vertically relative to its page size? I want to position it say for example x number of points from right and centered vertically. The text of course is rotated 90 degrees.
int n = reader.getNumberOfPages();
PdfImportedPage page;
PdfCopy.PageStamp stamp;
for (int j = 0; j < n; )
{
++j;
page = writer.getImportedPage(reader, j);
stamp = writer.createPageStamp(page);
Rectangle crop = reader.getCropBox(1);
// add overlay text
Phrase phrase = new Phrase("Overlay Text");
ColumnText.showTextAligned(stamp.getOverContent(), Element.ALIGN_CENTER, phrase,
crop.getRight(72f), crop.getHeight() / 2, 90);
stamp.alterContents();
writer.addPage(page);
}
The code above gives me inconsistent position of text, and in some pages, only a portion of the "Overlay text" is visible. Please help, I don't know how to properly use mediabox and cropbox and I'm new to itext.
Thanks!
Regarding the inconsistent position: that should be fixed by adding the vertical offset:
crop.getRight(72f), crop.getBottom() + crop.getHeight() / 2
Do you see? You took the right border with a margin of 1 inch as x coordinate, but you forgot to take into account the y coordinate of the bottom of the page (it's not always 0). Normally, this should fix the positioning problem.
Regarding the fact that only a portion of the overlay text is visible: my first guess was that you're adding content under the existing content, but that guess is wrong (you're using getOverContent()). What exactly do you mean by that second question? Do yo mean the text is clipped by the CropBox? Are you looking for a way to measure the content of phrase to see if it fits the height before you add it?
What is the recommended way of printing a text document as a pdf using absolute positioning ?
I am having a table that I have to print. I am also having the data type lengths and starting positions of the columns.
Since the existing table was a character based, there was no problem in its positioning. But even after using a monotype font (Courier, 10) I am not able to properly position the data and last column(s) of each row erroneously skip to the next line.
In order to present my data as close as the character one, I divided the page into different columns(based on its page size) and then add the contents at the desired place. I am adding chunks of data into the paragraph.
paragraph.add(new Chunk(new VerticalPositionMark(), columnNo*ptUnit, false));
I have tried to tweak the page size, font size and margin lengths, but the data is not properly displayed. Have you encountered any such problems ? please do share your thoughts.
Have you tried ColumnText
When i want to write a paragraph and I do know the amount of lines...I do a cycle incrementing (even it says incrementing and is minus is because the pdf is from "south" to "north" (0 - height) the y in a proportion of the fontsize, something like this
//_valueArray is my string[]
//fontSize is the value of the Size of the font...
//1.5 it's just a magic number :) that give me the space line that i need
//cbLocal is the PdfContentByte of the pdf
for (i = 0; i < _valueArray.Length; i++)
{
var p = new Phrase(_valueArray[i], font);
ColumnText.ShowTextAligned(cbLocal, align, p, x, y, 0);
if (i + 1 != _valueArray.Length)
{
y = y - (fontSize*1.5f);
}
}