Java image scaling - java

I am outputting images to a PDF file using iText. The images always appear larger than they are supposed to. According to the book (iText in Action), this is because iText always displays the images at a resolution of 72 dpi, regardless of what the actual dpi property of the image is. The book suggests using image.getDpiX() to find the dpi of the image, and then using image.scalePercent(72 / actualDpi * 100) to display the image properly. So far, the getDpiX() property of all my images have returned 0 (I've tried 2 gifs and 1 jpg). Is there another way to figure out the actual DPI so that my images scale properly?
com.lowagie.text.Image graphic = com.lowagie.text.Image.getInstance(imgPath);
float actualDpi = graphic.getDpiX();
if (actualDpi > 0)
//Never gets here
graphic.scalePercent(72f / actualDpi * 100);

According to the com.lowagie.text.Image JavaDoc, the method getDpiX gets the dots-per-inch in the X direction. Returns zero if not available.
You're going to have to assume a value when the getDpiX method returns zero. 100 dpi is as good an assumption as any.
if (actualDpi <= 0) actualDpi = 100f;
graphic.scalePercent(72f / actualDpi * 100f);

For GIF, there is no place to store a "DPI" information in the file, so "actualDpi" has no meaning in that case. For JPEG, the "DPI" information can be stored in the file, but is not mandatory, and if not set: "actualDPI" has no meaning as well.
The real answer is: there is no such thing as "actual DPI", either the information is provided (ie. "in this image I want 1 pixel to be rendered with this specific physical width (or height)"), or it's not. Another element is in your sentence: "always appear larger than they are supposed to"; the "supposed to" is the DPI information stored in the image. So if this information is absent, and you feel that when you open the image it seems right on the screen, then you have to calculate the density of your screen (width in number of pixels divided by the width in inches of your screen), and use that as your "actualDPI" variable.

The screen-resolution, retrieved by
java.awt.Toolkit.getDefaultToolkit().getScreenResolution()
did not help, image was still too big. However, one can scale the image to the page width in the following way:
BufferedImage bufferedImage = null; // your image
Image image = Image.getInstance(bufferedImage,null);
image.scalePercent((document.right()-document.left())/((float)image.getWidth())*100);

Related

How to get image dimension in inches without knowing the DPI?

I am new in android studio... I was searching as to how i can convert the dimensions of an image. I found out that you need to get the DPI. My question is will it be possible to convert the unit of the image, to inches, without knowing the DPI?? I tried using .getheight() and .getwidth() and it does give me the resolution, my problem is I need to convert this to inches. Also, I saw this "dp" in imageview... I'm wondering wheter this has an equivalent value in inches that I can use to convert my image using ratio and proportion. Also found out bitmap.getdensity(). Does that get the actual dpi of the image??
How to get image dimension in inches without knowing the DPI?
The question doesn't make much sense.
In general, a digital image has no inherent size in inches, or an inherent DPI (dots per inch). It has a size in pixels (or dots).
The DPI is actually a property of the device you are displaying the image on. For an image that is displayed, you multiply the DPI of the device by the number of pixels in the height or width of the image to give you the height / width of the image on the screen. (If the image has been scaled, the scaling factor needs to be taken into account too.)
In the case of images produced by digital cameras, the metadata of the image may include a "resolution height DPI" and "resolution width DPI". I couldn't find a explanation of what these are supposed to mean, but I surmise that they are either
the physical resolution of the capture device (e.g. CCD), or
a value chosen by the creator of the image to give you their preferred image size.
Neither of these is of much relevance when you are displaying an image. (IMO)

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.

Why are the values returned by getWidth() and getHeight() double the actual image dimensions?

I'm developing for Android, and my program involves a lot of bitmap images - these are set using BitmapFactory. One of my images is 80x30, and I assign it to a Bitmap variable wit the following instruction:
bmapImg = BitmapFactory.decodeResource(currContext.getResources(), R.drawable.brick);
When I then use the instruction
myImgWidth = bmapImg.getWidth();
myImgWidth is evaluated to 160 instead of 80. Likewise, when I run
myImgHeight = bmapImg.getHeight();
myImgHeight is evaluated to 60 instead of 30. I've looked through other questions on StackOverflow relating to these methods, but all of those issues were about getWidth() and getHeight() returning 0. Am I doing something wrong, or is there a reason as to why these functions return double the actual image dimensions?
You using decodeResource, so image is scaled based on your DPI. To scale it to size you need, place it in different folder. I suggest drawable_xhdpi is what you need, if scale factor is 2.
And keep in mind, it will be scaled on devices with different screen density.
To completely avoid scaling you may put your image to assets folder for example.
It is all about which screen you are running your app on. The values returned by those methods are the pixels that the image takes on the current screen. They will return values that are 1:1 with your image's resolution if you are running on a mdpi screen and you used 80dp and 30dp respectively for their dimensions. I would bet you are probably running your app on a xhdpi screen and do not have a resource for that density bucket
As mentioned in other answer, image is resized based on the device you check on.
If you don't want android to resize your image based on DPI, I recommend you to add your image in drawable-nodpi folder of your project

TIFF wrong height

OK, I agree this is a strange question, but bear with me.
I have a TIFF image and I am using Windows. When I right-click and go to Properties, I see Width = 1728 pixels and Height = 1146 pixels. When I read it with Java as a BufferedImage and call getWidth() and getHeight(), I see the same things. When I open it with MS Paint, I see the same size.
But the problem is that this size is not correct because the height is too small and the image looks idiotic. Now the interesting part: when I open it with IrfanView, I see it OK, with Height = 2292 pixels. When I open it with Windows Photo Viewer, it looks OK with Height = 2292 pixels.
So my question is: How did IrfanView and Windows Photo Viewer manage to recognize the correct height, although it was specified wrong in the metadata of the image? And how to do the same in Java? I don't want to show an idiotic image to the user.
P.S The image comes from outside and I can't do anything about the wrong metadata...
I resolved the problem. In fact the image was inappropriate from the beginning. The reason why IrfanView shows it OK, is that it compares the Horizontal and Vertical DPI and if they are not equal, it resizes the image to make them equal.
For example:
HorizontalDPI = 200
VerticalDPI = 100
Width = 800
Height = 600
When IrfanView opens the image it makes the following:
Height = Height * (HorizontalDPI / VerticalDPI) = 600 * 2 = 1200.
I ended up doing the same in my software. Everything is working fine:)
I hope this post will be useful to other people:)

fail reducing Image size using google app engine images java API

I want to reduce image size (in KB) when its size is larger than 1MB.
when I apply the resize transformation with smaller width and smaller height the size of the transformed image (in bytes) is larger than the orig image.
The funny (or sad) part is even when I invoke the resize with the same width and height as the orig (i.e. dimensions are not changed) the size "transformed" image is larger than the orig
final byte[] origData = .....;
final ImagesService imagesService = ImagesServiceFactory.getImagesService();
final Image origImage = ImagesServiceFactory.makeImage(oldDate);
System.out.println("orig dimensions is " + origImage.getWidth() + " X " + origImage.getHeight());
final Transform resize = ImagesServiceFactory.makeResize(origImage.getWidth(), origImage.getHeight());
final Image newImage = imagesService.applyTransform(resize, origImage);
final byte[] newImageData = newImage.getImageData();
//newImageData.length > origData.length :-(
Image coding has some special characteristics that you are observing the results from. As you decode a image from its (file) representation, you generate a lot of pixels. The subsequent encoding only sees the pixels and does not know anything about the size of your original file. Therefore the encoding step is crusial to get right.
The common JPEG format, and also the PNG format, have different compression levels, i.e a quality setting. They can have this because they do lossy compressions. In general, images with a lot of details (sharp edges) should be compressed with high quality and blurry images with low quality; as you probably have seen, small images usually are more blurry and large images usually more sharp.
Without going into the techical details, this means that you should set the quality level accoring to the nature of your image, which also is determined by the size of the input image. In other words, if you encode a blurry image as a big file, you are wasting space, since you would get about the same result using less bytes. But the encoder does not have this information, so you have to configure it using the correct quality setting
Edit: In your case manually set a low quality for encoding if you started with a small file (compared to number of pixels) and then of course a high quality if the opposite is true. Do some experimentations, probably a single quality setting for all photos will be acceptable.
A pitfall I fell in was, that I requested PNG output ... and the image size didn't change either. The image service silently ignored quality parameter. According to a comment in implementation the quality parameter is considered only for JPEG.

Categories

Resources