Getting DPI of PDPage/PDDocument to calculate PDF Dimensions Accurately - java

I'm looking to get an accurate size of each page in a PDF as part of a Unit test of PDF's I'll be creating. As I'm dealing with PDFs that have many different page sizes in each document the code returns an ArrayList of dimensions.
AFAIK each page can have its own DPI setting too.
I've done quite a bit of Googling but I've only come up with this which only gives me part of the answer, as I still need to work out what DPI each page is.
PDFBox - find page dimensions
public static ArrayList<float[]> getDimentions(PDDocument document) {
ArrayList<float[]> dimensions = new ArrayList<>();
float[] dim = new float[2];
//Loop Round Each Page
//Get Dimensions of each page and DPI
for (int i = 0; i < document.getNumberOfPages(); i++) {
PDPage currentPage = document.getPage(i);
PDRectangle mediaBox = currentPage.getMediaBox();
float height = mediaBox.getHeight();
float width = mediaBox.getWidth();
// How do I get the DPI now????
}
//Calculate Size of Page in mm (
//Get Dimensions of each page and DPI ( https://stackoverflow.com/questions/20904191/pdfbox-find-page-dimensions/20905166 )
//Add size of page to list of page sizes
//Return list of page sizes.
return dimensions;
}

The page dimensions (media box / crop box) are given in default userspace units which in turn default to 1/72 inch. So simply divide the box width or height by 72 to get the page width or height in inch.
This does not correspond to a DPI value of 72, though, because that would be a resolution value and a pdf does not have a resolution, the default userspace units merely are a different measurement unit.

Related

pdfbox, PDFRenderer.renderImage().getWidth() and PDImageXObject.getImage().getWidth() return numbers in different scale?

I use pdfbox to convert pdf into images and find the width returned by PDFRenderer and PDImageXObject seem to have different scales.
How do I get the widths in same scale?
This is how I get width of the page:
PDFRenderer pdRender = new PDFRenderer(pdDoc);
BufferedImage singlePage = pdRender.renderImage(pgIdx-1);
singlePage.getWidth(); // pageWidth = 623
and this is how I get width of the image block:
PDImageXObject image = (PDImageXObject) o;
image.getImage(); // imageWidth = 484
The "pageWidth" is the actual size as show in image metadata, but the "imageWidth" is larger than the real size. The actual ratio is shown in the following image (the whole page vs red box).
Your way to determine the page size
PDFRenderer pdRender = new PDFRenderer(pdDoc);
BufferedImage singlePage = pdRender.renderImage(pgIdx-1);
singlePage.getWidth(); // pageWidth = 623
is determining the page width in pixel after rendering the page as bitmap using some default settings, in particular at some unknown resolution.
Your way to determine the image dimension
PDImageXObject image = (PDImageXObject) o;
image.getImage(); // imageWidth = 484
is determining the actual dimensions of the bitmap resource without consideration of how it is used on the page if at all.
Thus, those numbers are entirely unrelated.
If you want to compare sizes on a PDF page, the natural choice of units would be the default user space units of a PDF page. By default they equal 1/72 inch.
You can retrieve the page size of a PDPage page in user space units like this:
PDRectangle cropBox = page.getCropBox();
float width = cropBox.getWidth();
float height = cropBox.getHeight();
The dimensions of a bitmap on a PDF page are a bit more difficult because a bitmap is subject to an arbitrary affine transformation, the current transformation matrix (CTM) at the time it is drawn. Thus, you have to determine that CTM value. To do so you have to parse the page content up to the point at which the bitmap is drawn, and right then you have to read the CTM from the current transformation matrix.
The PDFBox example PrintImageLocations demonstrates this, the output "displayed size = XXX, YYY in user space units" is the one you're looking for.

PDF Shrink causing change in Orientation

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.

JavaFX load image memory problems

When I load an 15Mb image in JavaFX, it takes like 250Mb of RAM!
Image imageColored = new Image("file:C:\\Users\\user\\Desktop\\portret.jpg");
ImageViewResizable imageView = new ImageViewResizable(imageColored);
And copying it takes 10 seconds and increases RAM usage up to 1Gb.
WritableImage imageBlack;
int width = (int) imageColored.getWidth();
int height = (int) imageColored.getHeight();
//make img black and white;
imageBlack = new WritableImage(width, height);
PixelReader pixelReader = imageColored.getPixelReader();
PixelWriter pixelWriter = imageBlack.getPixelWriter();
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
Color color = pixelReader.getColor(x, y);
double grey = (color.getBlue() + color.getGreen() + color.getRed()) / 3;
pixelWriter.setColor(x, y, new Color(grey, grey, grey, color.getOpacity()));
}
How can I decrease RAM usage and copy image effectively?
This is expected behaviour and has already been discussed in the JavaFX bug system. To overcome this you need to provide the size of the image to which the image should be scaled, in the Image()
constructor.
According to one of the comments by a JavaFX lead developer Kevin Rushforth :
The png image is encoded in a way that it needs to be decoded before it can be used or displayed. When you construct an Image, it has to create that buffer with W*H pixels. Each pixel takes up 4 bytes in memory. As specified the default Image constructor takes the width and height specified in the file as the width and height of an image. This is a 5000*5000 image meaning 25,000,000 pixels. At 4 bytes each, that takes 100 Mbytes in memory. The only way to reduce the memory is to scale the image on the way in by specifying a smaller width and height to which the image is scaled.
Although, he talks about PNG, creating buffer with W*H must not be very different for JPEG images.
For more information visit - Huge memory consumption when loading a large Image

How do i set text size in a text view programmatically according to the width of a text view.?

The text view i am using in the application is supposed to be single line and has a predefined width which may contain text fetched from database ranging from single word to maximum 3 words. I found a link in stack overflow for adjusting the text size according to height of the text view but i am looking for something more particular to width of text view.
This will work, but you might find faster ways posted. The incrementation in the loop does cost some time, so use sparingly in a single layout.
Display display = this.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x; //Get screen width. There are other ways to do this too.
int tSize = 10;
tv.setTextSize(tSize); //tv is your TextView
while (tv.getPaint().measureText((String) tv.getText())
< width){
tSize++;
tv.setTextSize(tSize); //Increment up till screen width is filled.
}

How to position text relative to page using iText?

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?

Categories