How to calculate/set font line margin itextPDF - java

I am implementing PdfPageEventHelper event and footer stuff is as below:
ColumnText.showTextAligned(cb, Element.ALIGN_RIGHT, new Phrase(String.format(" %d ",
writer.getPageNumber()),footerFont),
document.right() - 2 , document.bottom() - 20, 0);
Now, i have 3 lines which needs to be added into footer but i don't find a best to set its vertical margin. (Each 3 LINES has different font SIZE).
what should keep for - document.bottom() - XXX ??

The difference between two lines is the leading. You can pick your own leading, but it is custom to use 1.5 times the font size. You are drawing line by line yourself, using different font sizes, so you'll have to adjust the Y value based on that font size. Note that ColumnText.showTextAligned() uses the Y value as the baseline of the text you're adding, so if you have some text with font size of 12pt, you'd need to take into account a leading of 18pt. If you have a font size 8pt, you make sure you have 12pt.
That's the easy solution: based on "convention". If you really want to know how much horizontal space some specific takes, you need to calculate the ascender and the descender, as is done in figure 3.7 of my book. You'll find the code here. If bf is your font (a BaseFont object), text is your text (a String) and size is your font size (a float), then the height of your text is equal to height:
float aboveBaseline = bf.getAscentPoint(text, size);
float underBaseline = bf.getDescentPoint(text, size);
float height = aboveBaseline - underBaseline;
When y is the Y-coordinate used in showTextAligned() make sure you keep the space between y + aboveBaseline and y + underBaseline free. This is the accurate solution.
Note that document.bottom() - 20 looks somewhat strange. I would expect document.bottom() + 20 as the Y-axis of the PDF coordinate system points upwards, not downwards.

Related

Image overlay on pdf using itext java

I am trying to overlay an image on PDF pages. When I try to do that using adobe acrobat and select vertical distance from top and left equal to 0, then image overlays correctly at the required location.
I am trying to achieve the same using iText API but can't seem to position the image at correct location on the pdf.
The values for position are trail and error. The size of the pdf is 612X792 and the size of the image is 1699.0x817.0 so I scaled the image to fit the pdf size.
The left of the image and pdf align correctly but the tops have issue. I tried with all the values and somehow 792/2+100 matches this but again will change in case I get a different pdf or image.
Somehow adobe reader is able to do that. Is there a way to align left and top in iText or any other library.
The pdf is existing pdf generated from some other source.
Updated source code
public void manipulatePdfNoTransparency(String inputFileName,
String outputfileName, String overlayFilePath,
int altPage) throws IOException, DocumentException {
System.out.println("outputfileName :"+outputfileName);
PdfReader reader = new PdfReader(inputFileName);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputfileName));
stamper.setRotateContents(false);
// image watermark
Image img = Image.getInstance(overlayFilePath);
float yOffset=calculateYOffset(reader.getPageSize(1).getWidth(), reader.getPageSize(1)
.getHeight(),img.getWidth(),img.getHeight());
img.scaleToFit(reader.getPageSize(1).getWidth(), reader.getPageSize(1)
.getHeight());
Rectangle pagesize;
float x, y;
// loop over every page
//int i=1;
pagesize = reader.getPageSize(1);
x = (pagesize.getLeft() + pagesize.getRight()) / 2;
y = (pagesize.getTop() + pagesize.getBottom()) / 2;
img.setAbsolutePosition(0,yOffset);
for (int i = 1; i <= n; i = i + altPage) {
stamper.getUnderContent(i).addImage(img);
}
stamper.close();
reader.close();
System.out.println("File created at "+outputfileName);
}
public static float calculateYOffset(float pdfWidth,float pdfHeight, float originalImageWidth,float originalImageHeight) {
// size of image 1699.0x817.0
// size of pdf 612X792
//This means that the scaled image has a height of 817 * (612/1699) = ca. 294.3 PDF coordinate system units.
System.out.println("pdfWidth : "+pdfWidth+ " pdfHeight : "+pdfHeight+" originalImageWidth : "+originalImageWidth+" originalImageHeight : "+originalImageHeight);
float scaledImageHeight = originalImageHeight*pdfWidth / originalImageWidth;
//The image shall be positioned on the page by putting its top left corner onto the top left corner of the page.
//Thus, the x coordinate of its lower left corner is 0, and the y coordinate of its lower left corner is
//the y coordinate of the upper left corner of the page minus the height of the scaled image,
//i.e. ca. 792 - 294.3 = 497.7.
float yOffset = pdfHeight-scaledImageHeight;
System.out.println("yoffset : "+ yOffset);
return yOffset;
}
First let's take a look at this line:
img.scaleToFit(
reader.getPageSize(1).getWidth(),
reader.getPageSize(1).getHeight());
The scaleToFit() method resizes an images keeping the aspect ratio intact. You seem to overlook that, so let me give you an example of what it means to keep the aspect ratio intact.
Suppose that you have an image img400x600 that measures 400 x 600 user units, and you scale that image to fit a rectangle of 200 x 10,000 user units:
img400x600.scaleToFit(200, 10000);
What will be the size of image400x600? You seem to think that the size will be 200 x 10,000, but that assumption is incorrect. The new size of the image will be 200 x 300, because the aspect ratio is width = 0.66666 * height.
You complain that the size of the image doesn't equal the size of the page when you use scaleToFit(), but that is normal if the aspect ratio of the image is different from the aspect ratio of the page.
If you really want the image to have the same size of the page, you need to use the scaleAbsolute() method:
img.scaleAbsolute(
reader.getPageSize(1).getWidth(),
reader.getPageSize(1).getHeight());
However, this might result in really distorted images, because scaleAbsolute() doesn't respect the aspect ratio. For instance: I have seen developers who used scaleAbsolute() on portraits of people, and the result was that the picture of these people became ugly; either their head became extremely fat, or it became extremely thin depending on the different in aspect ratio.
Now let's take a look at this line:
img.setAbsolutePosition(0,y+100);
That is a very strange line. You are making the assumption that the x coordinate of the lower left corner is 0; I understand that y + 100 was obtained through trial and error.
Let's see what the official documentation has to say about defining the offset of objects added to an existing PDF document. There are several FAQ items on this subject:
Where is the origin (x,y) of a PDF page?
How to position text relative to page?
How should I interpret the coordinates of a rectangle in PDF?
...
You currently only look at the value of the /MediaBox (you obtain this value using the getPageSize() method), and you ignore the /CropBox (in case it is present).
If I were you, I'd do this:
Rectangle pageSize = reader.getCropBox(pageNumber);
if (pageSize == null)
pageSize = reader.getPageSize(pageNumber);
Once you have the pageSize, you need to add the take into account the offset when adding content. The origin might not coincide with the (0, 0) coordinate:
img.setAbsolutePosition(pageSize.getLeft(), pageSize.getBottom());
As you can see: there is no need for trial and error. All the values that you need can be calculated.
Update:
In the comments, #mkl clarifies that #Gagan wants the image to fit the height exactly. That is easy to achieve.
If the aspect ratio needs to be preserved, it's sufficient to scaleToFit the height like this:
img.scaleToFit(100000f, pageSize.getHeight());
In this case, the image won't be deformed, but part of the image will not be visible.
If the aspect ratio doesn't need to be preserved, the image can be scaled like this:
img.scaleAbsolute(pageSize.getWidth(), pageSize.getHeight());
If this still doesn't answer the question, I suggest that the OP clarifies what it is that is unclear about the math.
In a comment to the question I mentioned
somehow 792/2+100 matches this - actually that is off by about 1.7. You only need very simple math to calculate this.
and the OP responded
when you say it is off by 1.7 and simple math is required to calculate this. could you please let me know what math and how you arrived at 1.7.
This answer explains that math.
Assumed requirements
From the question and later comments by the OP I deduced these requirements:
An image shall be overlayed over a PDF page.
The image for this shall be scaled, keeping its aspect ratio. After scaling it shall completely fit onto the page and at least one dimension shall equal the corresponding page dimension.
It shall be positioned on the page by putting its top left corner onto the top left corner of the page, no rotation shall be applied.
The crop box of the PDF page coincides with the media box.
Calculation at hand
In the case at hand, the size of the pdf is 612X792 and the size of the image is 1699.0x817.0. Furthermore, the OP's comments imply that the bottom left corner of the page actually is the origin of the coordinate system.
To scale the horizontal extent of the image to exactly fit the page, one has to scale by 612/1699. To scale the vertical extent to exactly fit the page, one has to scale by 792/817. To make the whole image fit the page with aspect ration kept, one has to use the smaller scaling factor, 612/1699. This is what the OP's
img.scaleToFit(reader.getPageSize(1).getWidth(),reader.getPageSize(1).getHeight());
does, assuming the crop box coincides with the media box.
This means that the scaled image has a height of 817 * (612/1699) = ca. 294.3 PDF coordinate system units.
When positioning an image on a PDF page, you usually do that by giving the coordinates where the bottom left corner of the image shall go.
The image shall be positioned on the page by putting its top left corner onto the top left corner of the page. Thus, the x coordinate of its lower left corner is 0, and the y coordinate of its lower left corner is the y coordinate of the upper left corner of the page minus the height of the scaled image, i.e. ca. 792 - 294.3 = 497.7.
Thus, the scaled image shall be positioned at (0, 497.7).
The numbers the OP found by trial and error are 0 for x and middle height plus 100 for y. The middle height is (792 + 0)/2 = 396. Thus, he uses the coordinates (0, 496) which (see above) vertically are off by ca. 1.7.

Opencv Java adjusting rectangle dimensions

I am working on licence plate detection in OpenCv, currently I can detect a licence plate Sample of detected licence plate
But the issue is the rectangle is too close to the licence plate characters, what I thought was I could just increase the dimensions by a given offset of which I did Sample of increased offset detection
But unfortunately my understanding of the Rectis different from how it works, unlike the circle where you have a single point where it's drawn from, the rectangle uses 2 points, of which after increasing the dimensions, if shifts to the right(At least that is what it seems), I need help on centering the rectangle on its original location after increasing the offset, here is the code am using to increase it's dimensions
rect.height = (int) (rect.height * 1.1);
rect.width = (int) (rect.width * 1.5);
Imgproc.rectangle(originalFrame, rect.br(), rect.tl(), new Scalar(0,0,255), 2);
I don't have enough rep yet so images do not display automatically.
If you increase the right side's x coordinate by length, then you need to make all of the rectangle's coordinates go left length/2. The same applies to height.
rect.xCoord = rect.xCoord - ((rect.width * 1.1) - rect.width) / 2)
This goes before the first line of code you posted above. I'm not sure if this is how you access x coordinates in open cv (since I don't know it), so replace the x coord access with the actual one if this is wrong.

iText Maximum Font Size

I'm using a fixed cell height to create a table.
If the font size is too large, the text is not visible in the table.
Is there a built-in function in iText that automatically reduces the font size to the maximum possible size, or do I have to implement this by myself?
Automatic font size is only possible in the context of AcroForm text fields. When you define the font size of a text field as 0, then a font size is chosen that fits the rectangle. In the case of a fixed cell height in a table, you are responsible to make sure that the text fits.
If you're concerned about the height, please take a look at the FitTextInRectangle example:
BaseFont bf = BaseFont.createFont();
int textHeightInGlyphSpace = bf.getAscent(text) - bf.getDescent(text);
float fontSize = 1000f * fixedHeight / textHeightInGlyphSpace;
This example was written in answer to Correct text position center in rectangle iText
If you're concerned about the width, then you need to use the getWidthPoint() method as explained here: How to calculate the string width in iText?
BaseFont bf = BaseFont.createFont();
float width = bf.getWidthPoint("My text", myFontSize);
You'll need to make sure that width doesn't exceed the width of the cell. To achieve this, you'll need to adjust myFontSize.
See my answer to this question: How to choose the optimal size for a font?

Resize font based on string length

From an xml file, I'm given a width, height and id. All of them can and do vary very quickly. Now, I'm asked to draw a rectangle using the width and height (an easy task), and place the id at its center. The id must not overflow out of the rectangle it's contained it.
For single-character strings, this is also easy - set the font size to the height, play a bit with the x position maybe, and it's centered. The problem is when it's multi-character strings.
So given a width and height and a string, how can you determine what font-size the string should appear in? Assume you have every bit of information you need on the rectangle you're drawing the string in.
[Edit]: I'm using the Graphics 2D class to draw everything.
Start with selecting a Font at your preferred (i.e. maximum) size.
Grab the FontRenderContext from your Graphics2D object using getFontRenderContext.
Use getStringBounds() on the Font to be rendered to get a Rectangle2D object for the specific String to be rendered. That object describes the final size of the String using that Font
Check if the size specified by that Rectangle2D is small enough.
4a. If it is small enough, you're done. Use the last Font you've checked.
4b. If it is too big, use Font.derive() to produce a smaller version of the Font and continue to use that and loop back to 3.
Don't quite have the time to give you a full working example, but here are a couple pointers that should get you going in the right direction. The graphics object you are using to draw with has a getFontMetrics() method, one of the methods on FontMetrics is stringWidth(String str) which gives you the width of a string in the current Font.
If the width is too big for your rectangle set the Font on the Graphics object to the same font just with a smaller size until it fits.
To horizontally center a string in a container (learned long ago in typing class in high school):
(rectangleWidth / 2) - (stringWidth / 2)
http://download.oracle.com/javase/1.5.0/docs/api/java/awt/FontMetrics.html
To create a Font with a smaller size, something like:
Font font = graphics.getFont();
Font smallerFont = font.derive(font.getSize() - 1);
graphics.setFont(smallerFont);
Hope this gets you going in the right direction.
I would recommend for this problem to remove as many unknowns as possible. In this case, the problem chiefly is that font characters can vary in width... well most. That's why I would use a good monospace font like courier new for the ID, that way you know what the width of each character is, you know the width of your rectangle and you know the number of characters in your string. You can simply reduce the pixel size of each character will till your string fits the available width.
Example, if the width of each character is 12px and you have 10 characters in your ID, then you need 120px to fit everything in. If you only have 80px available, it's simple math 80/10 = 8px font-size (reduce half a pixel for padding if you want.
Just my suggestion.

Calculate possible number of lines in RitchTextField

I have a screen with a RitchTextField added, which has a custom font applied. I want to find the total number of lines that the RitchTextField can hold to avoid vertical scrolling.
I tried to do this by getting the height of the RichTextField then dividing it by the the height of the font, the problem however is using rtfField.getHeight() always returns the current height of field, and using the screens getHeight() returns the screen size not taking other fields into consideration.
For example:
Screen Size using getHeight() = 360.
Font Size using: this.rtfField.getFont().getHeight() = 25
Total Lines ~ 12
However doing a manual count the screen only comfortably displays 8 lines.
Not sure if this is what you're looking for, but I think you want the current height of the field divided by the font height:
int lines = this.rtfField.getHeight() / this.rtfField.getFont().getHeight();

Categories

Resources