Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iText: Reduce image quality (for reducing the resulting PDF size)

What is the best practice for reducing the size of JPEG images in a PDF file, newly created using iText? (My objective is a trade-off between image quality and file size.)

The images are created as follows:

Image image = new Image(ImageDataFactory.create(imagePath))

I would like to provide a scale factor, for instance 0.5, which halves the number of pixels in a row.

Say I generate a PDF with a single 3 MB image. I tried image.scale(0.5f, 0.5f), but the resulting PDF file is still roughly 3 MB. I expected it to become much smaller.

Thus I guess the source image, embedded in the PDF file, is not touched. But that is what I need: The total number of pixels in the entire PDF file stored on disk should be reduced.

What is the easiest/recommended way to achieve this?

like image 684
ideaboxer Avatar asked Mar 19 '18 23:03

ideaboxer


People also ask

Is iText free for commercial use?

This license is a commercial license. You have to pay for it. To answer your question: iText can be used for free in situations where you also distribute your software for free. As soon as you want to use iText in a closed source, proprietary environment, you have to pay for your use of iText.

What is iText leading?

According to the PDF specification, the distance between the baseline of two lines is called the leading. In iText, the default leading is 1.5 times the size of the font. For instance: the default font size is 12 pt, hence the default leading is 18.

What is iText format?

iText is a Java library originally created by Bruno Lowagie which allows to create PDF, read PDF and manipulate them. The following tutorial will show how to create PDF files with iText. This tutorial assumes that you have basis Java and Eclipse knowledge. iText has a hierarchical structure.


1 Answers

Scale the image first, then open the scaled image with iText.

There is a create method in ImageDataFactory that accepts an AWT image. Scale the image using AWT tools first, then open it like this:

String imagePath = "C:\\path\\to\\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));

// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null); 
g.dispose();

/* 
Optionally pick a color to replace with transparency.
Any pixels that match this color will be replaced by tansparency.
*/
Color bgColor = Color.WHITE;

Image itextImage = new Image(ImageDataFactory.create(scaledAwtImage, bgColor));

For better tips on how to scale an image, see How can I resize an image using Java?

If you still need the original size when adding to PDF, just scale it back up again.

itextImage.scale(2f, 2f);

Note: This code is untested.


EDIT in response to comments on bounty

You got me thinking and looking. It appears iText treats importing an AWT image as a raw image. I presume it treats it the same as a BMP, which simply writes the pixel data using /FlateDecode, which is probably significantly less than optimal. The only way I can think of to achieve your requirement would be to use ImageIO to write the scaled image to the file system or a ByteArrayOutputStream as a jpeg, then use the resultant file/bytes to open with iText.

Here's an updated example using byte arrays. If you want to get any more fancy with compression levels and such, refer here.

String imagePath = "C:\\path\\to\\image.jpg";
java.awt.Image awtImage = ImageIO.read(new File(imagePath));

// scale image here
int scaledWidth = awtImage.getWidth(null) / 2;
int scaledHeight = awtImage.getHeight(null) / 2;
BufferedImage scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null); 
g.dispose();

ByteArrayOutputStream bout = new ByteArrayOutputStream()
ImageIO.write(scaledAwtImage, "jpeg", bout);
byte[] imageBytes = bout.toByteArray();

Image itextImage = new Image(ImageDataFactory.create(imageBytes));
like image 123
Ben Ingle Avatar answered Oct 13 '22 11:10

Ben Ingle