Get overflow text from OpenPDF - java

I'm trying to set up a page where the text fills up in a text box and then overflows to another text box on the page. I set up a rectangle and will send it the text, but I can't figure out how to find the text that won't fit.
I remember seeing a function that adds words to a document (or table or something) and it had a "Returns remaining text" feature. I can't find that function anymore.
How can I get the text that won't fit returned so I can assign it to a variable to send to the next textbox?

You are looking for ColumnText which can be used to render text inside a defined shape. The go() method returns the constants NO_MORE_TEXT (1) if the text fit inside the shape or NO_MORE_COLUMN (2) if the text is overflowing. As far as I know there is no way to actually get the remaining text directly. However, you don't need the text for rendering it in the next textbox.
In case the text overflows, you can just assign a new column and call go() again. Repeat this process until all the text has been drawn.
Here is an example with 3 simple rectangular boxes:
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("overflow.pdf"));
document.open();
// define text
PdfContentByte canvas = writer.getDirectContent();
ColumnText ct = new ColumnText(canvas);
ct.addElement(new Paragraph(LOREM_IPSUM)); // <- some filler text from constant
// fill first box
ct.setSimpleColumn(50, 735, 400, 800);
int result = ct.go();
// fill second box (if necessary)
if (result == ColumnText.NO_MORE_COLUMN) {
ct.setSimpleColumn(75, 635, 425, 700);
result = ct.go();
}
// fill third box (if necessary)
if (result == ColumnText.NO_MORE_COLUMN) {
ct.setSimpleColumn(100, 535, 450, 600);
result = ct.go();
}
document.close();
Result:

Related

iText 7: Paragraph height as it would be rendered

I can set the width of a new paragraph as follows, which results in a certain height:
Paragraph p = new Paragraph("some longer text some longer text some longer text");
p.setWidth(100);
System.out.println("height " + p.getHeight());
document.add(p);
Of course p.getHeight() is null, since the rendered height is calculated during rendering the PDF file. But I need the height before the final rendering. How can I get it most efficiently?
To get the effective width of the paragraph as if it was drawn on a page already, you need to create renderer tree from model element tree, and then layout the topmost renderer. This is how it's done in code:
Paragraph p = new Paragraph("some longer text some longer text some longer text");
p.setWidth(100);
// Create renderer tree
IRenderer paragraphRenderer = p.createRendererSubTree();
// Do not forget setParent(). Set the dimensions of the viewport as needed
LayoutResult result = paragraphRenderer.setParent(document.getRenderer()).
layout(new LayoutContext(new LayoutArea(1, new Rectangle(100, 1000))));
// LayoutResult#getOccupiedArea() contains the information you need
System.out.println("height " + result.getOccupiedArea().getBBox().getHeight());
Please note that the computed dimensions will also include margins (present in a paragraph by default), so if you want to get the height without margins you should first set paragraph margin to 0:
p.setMargin(0);

PDFBox 2 - How do I remove borders from form fields before flattening?

I am using a PDF document as a template, with a form in it, and after I put the values into the form I would like to remove the field borders, before flattening the form. The borders are useful while editing, but they are horrible when the form is "rendered". I cannot change the colour of the borders to the background colour as it's not uniform.
The code below is modified from the AddBorderToField.java example from the source code download:
PDAnnotationWidget widget = textBox.getWidgets().get(0); // only the first widget for simplicity
PDAppearanceCharacteristicsDictionary appearanceCharacteristics = widget.getAppearanceCharacteristics();
if (appearanceCharacteristics == null)
{
// if it doesn't exist, create it
appearanceCharacteristics = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
}
appearanceCharacteristics.setBorderColour(new PDColor(new float[]{}, PDDeviceRGB.INSTANCE)); // empty array = invisible color
widget.setAppearanceCharacteristics(appearanceCharacteristics);
acroForm.refreshAppearances(); // alternatively, set the value of the field

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.

How to add text to an image?

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

Categories