PDFbox overlaped links at index document - java

I'm completely newbie to PDFBox and I'm having an issue I can't find the way to solve by the moment.
I get from my database a list of folder and documents located in those folders, I iterate over all these data to generate an index with active links to the correct folder/document path. (Imagine I have a root folder and I want to have a pdf index in that root with relative links to all folders and documents contained in it)
The main code looks like follows:
try {
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDFont font = PDType1Font.HELVETICA;
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(100, 700);
contentStream.drawString("Índice " + expediente.getNombre());
contentStream.endText();
int i = 0;
for (Folder currFol : root.getFolders()) {
for (Document currDoc : currFol.getDocuments()) {
i++;
float x = 50;
float y = 250;
String text = currFol.getName() + "/" + currDoc.getName();
float textWidth = font.getStringWidth(text) / 1000 * 12;
PDAnnotationLink link = new PDAnnotationLink();
PDGamma colourBlue = new PDGamma();
colourBlue.setB(1);
link.setColour(colourBlue);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(currFol.getName() + "/" + currDoc.getName());
link.setAction(action);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y -(i* 5));
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 50);
link.setRectangle(position);
page.getAnnotations().add(link);
}
}
// Make sure that the content stream is closed:
contentStream.close();
document.save(output);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
My problem here is that all elements are printed ovelaped, text and boxes are over each other and by the moment can't find out how to print correctly all links in a list formatted style to create the index.
Any idea or suggestion will be very apreciated.
I tried following some tutorials, with no succes by the moment, like http://www.programcreek.com/java-api-examples/index.php?api=org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink, http://www.massapi.com/class/pd/PDAnnotationLink.html .
I tried without PDRectangle, just the text link (as PDRectangle is present in all examples I found but I don't need it really)
Thank you,

In your inner loop you always set x and y to the same values
float x = 50;
float y = 250;
which you don't change thereafter. Then you draw the respective text starting at x, y
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
Thus, you draw each entry starting at the same coordinates. So it is not surprising all the text overlaps.
Furthermore you set position and size of the links like this:
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y -(i* 5));
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 50);
link.setRectangle(position);
Thus, the upper y coordinate of each link is always y + 50, i.e. 300, and the lower y coordinate of the links moves down by 5 per iteration. So the vertical extent of your first link is contained in that of the second which in turn is contained in that of the third etc etc etc. Again no surprise that these annotations overlap.
Thus, this has nothing to do with being a PDFBox newbie but merely with getting one's coordinates right... ;)
How about something like this instead:
float x = 50;
float y = 650;
for (Folder currFol : root.getFolders()) {
for (Document currDoc : currFol.getDocuments()) {
String text = currFol.getName() + "/" + currDoc.getName();
float textWidth = font.getStringWidth(text) / 1000.0 * 12;
PDAnnotationLink link = new PDAnnotationLink();
PDGamma colourBlue = new PDGamma();
colourBlue.setB(1);
link.setColour(colourBlue);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(currFol.getName() + "/" + currDoc.getName());
link.setAction(action);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y - 3);
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 12);
link.setRectangle(position);
page.getAnnotations().add(link);
y -= 15;
}
}

Related

Retrieve the coordinates of a Paragraph in iText

I'm trying to retrieve the x, y coordinates from a Paragraph created in iText. I followed the approved answer in How to get vertical cursor position when writing document in iText 7? but I'm not getting the expected result.
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("output/ITextSandbox/Coordinates.pdf"));
pdfDoc.setDefaultPageSize(PageSize.LETTER); // 8.5 x 11
Document document = new Document(pdfDoc);
PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER, PdfEncodings.UTF8);
document.setFont(font);
document.setFontSize(10);
Paragraph paragraph = null;
// Print 5 lines to ensure the y coord is sufficiently moved away from the top of the page.
for (int i = 1; i <= 5; i++)
{
paragraph = new Paragraph();
paragraph.add(new Text("Line " + i));
document.add(paragraph);
}
// Print a new paragraph from which to obtain the x, y coordinates.
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
// Follow the steps from the approved answer in
// https://stackoverflow.com/questions/51953723/how-to-get-vertical-cursor-position-when-writing-document-in-itext-7
IRenderer renderer = paragraph.createRendererSubTree().setParent(document.getRenderer());
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult = renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
float y = layoutResult.getOccupiedArea().getBBox().getY();
float x = layoutResult.getOccupiedArea().getBBox().getX();
System.out.println("x = " + x + ", y = " + y); // y should be approximately 630, not 710.
With standard margins and 10 pt font, the coordinates for the 6th line should approximately be x = 0, y = 630. Instead, I get y = 710.
The code sample in the question simulates rendering on a certain layout area. That approach works to determine the coordinates on the page if the exact same elements are simulated on the exact same layout area.
In the question, only the 6th paragraph is simulated, so the larger y coordinate (higher on the page) is expected. To get the correct y coordinate, the 5 preceding paragraphs would also have to be simulated.
Moreover, the layout area is not the same as page. This code does not take into account the page margins correctly:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
With the default page margins of 36, the correct layout area would be:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(36, 36, width, height))));
A much easier way to get the current coordinates after rendering some elements is:
/*...*/
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
Rectangle remaining = document.getRenderer().getCurrentArea().getBBox();
float y = remaining.getTop();
System.out.println("y = " + y);
Result: y = 631.6011
To illustrate the remaining layout area, let's draw it on the page:
PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(1));
canvas.setStrokeColor(ColorConstants.RED).rectangle(remaining).stroke();
With some different page margins:
document.setMargins(5, 25, 15, 5);

Rotate watermark text at 45 degree angle across the center Apache PDFBox

I want to add a text to the PDF using PDFBox API and rotate it by 45 Degree and place it at the center of the page, The text is dynamic and should be placed in the center always, I got everything else to work except centering piece, I'll appreciate any help.
I have this code:
Point2D.Float pageCenter = getCenter(page);
float stringWidth = getStringWidth(watermarkText, font, fontSize);
float textX = pageCenter.x - stringWidth / 2F + center.x;
System.out.println(textX);
float textY = pageCenter.y + center.y;
//System.out.println("Inside cross"+textX+", "+textY);
fontSize = 110.0f;
cs.transform(Matrix.getRotateInstance(Math.toRadians(45), textX, textY));
cs.moveTo(0, 0);
cs.lineTo(125, 0);
r0.setNonStrokingAlphaConstant(0.20f);
This is the result i want:
Output PDF
What I do is to first rotate based on the calculated angle. In this "rotated world" I do a horizontal offset so that the text is in the middle, and also move the text vertically a bit lower, so that it is in the "vertical" middle of an imagined diagonal line (horizontal in the "rotated world").
try (PDDocument doc = new PDDocument())
{
PDPage page = new PDPage();
doc.addPage(page);
PDFont font = PDType1Font.HELVETICA_BOLD;
try (PDPageContentStream cs =
new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true))
// use this long constructor when working on existing PDFs
{
float fontHeight = 110;
String text = "Watermark";
float width = page.getMediaBox().getWidth();
float height = page.getMediaBox().getHeight();
int rotation = page.getRotation();
switch (rotation)
{
case 90:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(90), height, 0));
break;
case 180:
cs.transform(Matrix.getRotateInstance(Math.toRadians(180), width, height));
break;
case 270:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(270), 0, width));
break;
default:
break;
}
float stringWidth = font.getStringWidth(text) / 1000 * fontHeight;
float diagonalLength = (float) Math.sqrt(width * width + height * height);
float angle = (float) Math.atan2(height, width);
float x = (diagonalLength - stringWidth) / 2; // "horizontal" position in rotated world
float y = -fontHeight / 4; // 4 is a trial-and-error thing, this lowers the text a bit
cs.transform(Matrix.getRotateInstance(angle, 0, 0));
cs.setFont(font, fontHeight);
//cs.setRenderingMode(RenderingMode.STROKE); // for "hollow" effect
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setNonStrokingAlphaConstant(0.2f);
gs.setStrokingAlphaConstant(0.2f);
gs.setBlendMode(BlendMode.MULTIPLY);
cs.setGraphicsStateParameters(gs);
// some API weirdness here. When int, range is 0..255.
// when float, this would be 0..1f
cs.setNonStrokingColor(255, 0, 0);
cs.setStrokingColor(255, 0, 0);
cs.beginText();
cs.newLineAtOffset(x, y);
cs.showText(text);
cs.endText();
}
doc.save("watermarked.pdf");
}
Note that I've set both stroking and non stroking (= fill). This is useful for people who want to try the (disabled) "hollow" appearance, that one uses stroking only. The default mode is fill, i.e. non-stroking.

How to write a text to end of generated pdf doc using PDFBox?

Using PDPageContentStream I am able to place the image and text at the top of the generated pdf page, but I am not able to place the the text to the end of pdf document.
How do I get the exact x, y values for invoking moveTextPositionByAmount(x,y)? See the sample code below
PDDocument document = new PDDocument();
PDXObjectImage img = new PDJpeg(document, new FileInputStream("F:/Project/demo_logo.jpg"));
PDPage imagePage = PDFUtility.addNewPage(document);
PDPageContentStream contentStream = new PDPageContentStream(document, imagePage);
contentStream.beginText();
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 14);
contentStream.moveTextPositionByAmount(10, 680);
contentStream.drawString("Budget Report");
contentStream.endText();
contentStream.drawImage(img, 10, 700);
float yStartNewPage = imagePage.findMediaBox().getHeight() - (12 * 10);
float tableWidth = imagePage.findMediaBox().getWidth() - (2 * 10);
boolean drawContent = true;
float yStart = yStartNewPage;
float bottomMargin = 500;
BaseTable table = new BaseTable(yStart,yStartNewPage, bottomMargin, tableWidth, 10, document, imagePage, true, drawContent);
.............generating reports using table(BaseTable)..............
table.draw();
File file = new File("F:/project/demo.pdf");
Files.createParentDirs(file);
resultArray.put(file.getAbsolutePath());
contentStream.beginText();
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 14 );
/* reports are generating dynamically, i cannot give value to x and y */
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString("End Of Report");
contentStream.endText();
contentStream.close();
document.save(String.valueOf(file));
document.close();

Font width of non-English fonts

I am trying to draw characters from Hindi on a plain white background and store the resulting image as a jpeg. I need to size the image dynamically so that it fits the text. Currently, image height is fixed by assuming a size of 35 pixels (font size has been set to 22). How do I fix image width?
So far, I have tried to set the image width to 35 pixels times the maximum length of the different lines of text. That has not worked and the images saved are very wide. I am using drawString method of the graphics class in Java.
The function that creates images:
public static void printImages_temp(List<String> list) {
/* Function to print translations contained in list to images.
* Steps:
* 1. Take plain white image.
* 2. Write English word on top.
* 3. Take each translation and print one to each line.
*/
String dest = tgtDir + "\\" + list.get(0) + ".jpg"; //destination file image.
int imgWidth_max = 410;
int imgHeight_max = 230;
int fontSize = 22;
Font f = new Font("SERIF", Font.BOLD, fontSize);
//compute height and width of image.
int img_height = list.size() * 35 + 20;
int img_width = 0;
int max_length = 0;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).length() > max_length) {
max_length = list.get(i).length();
}
}
img_width = max_length * 20;
System.out.println("New dimensions of image = " + img_width + " " + img_height);
BufferedImage img = new BufferedImage(img_width, img_height, BufferedImage.TYPE_INT_RGB);
Graphics g = img.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img_width, img_height);
//image has to be written to another file.
for(int i = 0; i < list.size(); i++) {
g.setColor(Color.BLACK);
g.setFont(f);
g.drawString(list.get(i), 10, (i + 1) * 35);
}
//g.drawString(translation, 10, fontWidth); //a 22pt font is approx. 35 pixels long.
g.dispose();
try {
ImageIO.write(img, "jpeg", new File(dest));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("File written successfully to " + dest);
}
My questions:
How do I get width of a non-Latin type character, given that the fonts used to render the text are generic?
Is there a way to get the width that applies to all UTF-8 characters?

Why is iText's PdfWriter printing JTextFields black when exported as a runnable jar?

I hava got a strange conundrum. I am currently trying to make a Runnable Jar of an Eclipse project I'm working on that has a number of JTextField's and JFormattedTextField's all nicely arranged in JPanel's. I am then taking these JPanel's and printing them out use iText's nice PdfWriter.
The issue is this: In Eclipse, this prints out just fine. When I export my project into a runnable jar, I get the following:
All of those black rectangles are where my JTextField's and JFormattedTextField's are located.
Does anyone have any idea what may be causing this? This is how I am printing things out, and remember, it is working in Eclipse but not when exported as runnable jar:
private void print() throws DocumentException, IOException {
Document document = new Document(PageSize.LETTER, 0, 0, 0, 0);
File file = new File(System.getProperty("user.home") + File.separator
+ "Desktop" + File.separator + "temp.pdf");
if (!file.exists()) {
file.createNewFile();
}
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream(file));
document.open();
PdfContentByte contentByte = writer.getDirectContent();
int x = (int) document.getPageSize().getWidth() / 2 - 250;
int y = (int) document.getPageSize().getHeight() - 375;
for (int i = 0; i < jobs.size(); i++) {
jobs.get(i).setFocusable(false);
contentByte.moveTo(0, y + 235);
contentByte.lineTo(document.getPageSize().getWidth(), y + 235);
contentByte.closePathStroke();
y += 5;
PdfTemplate template = contentByte.createTemplate(600, 250);
#SuppressWarnings("deprecation")
Graphics2D g2 = template.createGraphics(600, 250);
jobs.get(i).print(g2);
g2.dispose();
contentByte.addTemplate(template, x, y);
y -= 105;
jobs.get(i).setFocusable(true);
}
contentByte.moveTo(0, y + 235);
contentByte.lineTo(document.getPageSize().getWidth(), y + 235);
contentByte.closePathStroke();
y = 25;
PdfTemplate template = contentByte.createTemplate(600, 25);
#SuppressWarnings("deprecation")
Graphics2D g2 = template.createGraphics(600, 25);
total.setFocusable(false);
totalPanel.setOpaque(false);
totalPanel.print(g2);
g2.dispose();
contentByte.addTemplate(template, x - 65, y);
total.setFocusable(true);
totalPanel.setOpaque(true);
document.close();
}
Thanks for all of your help!

Categories