0

I am using iText to compress the existing pdf. I'm using the example from the iText in Action book (second edition):

package part4.chapter16;

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNumber;
import com.itextpdf.text.pdf.PdfObject;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfImageObject;

public class ResizeImage {

    /** The resulting PDF file. */
    public static String RESULT = "results/part4/chapter16/resized_image.pdf";
    /** The multiplication factor for the image. */
    public static float FACTOR = 0.5f;

    /**
     * Manipulates a PDF file src with the file dest as result
     * @param src the original PDF
     * @param dest the resulting PDF
     * @throws IOException
     * @throws DocumentException 
     */
    public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
        PdfName key = new PdfName("ITXT_SpecialId");
        PdfName value = new PdfName("123456789");
        // Read the file
        PdfReader reader = new PdfReader(SpecialId.RESULT);
        int n = reader.getXrefSize();
        PdfObject object;
        PRStream stream;
        // Look for image and manipulate image stream
        for (int i = 0; i < n; i++) {
            object = reader.getPdfObject(i);
            if (object == null || !object.isStream())
                continue;
            stream = (PRStream)object;
            if (value.equals(stream.get(key))) {
                PdfImageObject image = new PdfImageObject(stream);
                BufferedImage bi = image.getBufferedImage();
                if (bi == null) continue;
                int width = (int)(bi.getWidth() * FACTOR);
                int height = (int)(bi.getHeight() * FACTOR);
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR);
                Graphics2D g = img.createGraphics();
                g.drawRenderedImage(bi, at);
                ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
                ImageIO.write(img, "JPG", imgBytes);
                stream.clear();
                stream.setData(imgBytes.toByteArray(), false, PRStream.NO_COMPRESSION);
                stream.put(PdfName.TYPE, PdfName.XOBJECT);
                stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
                stream.put(key, value);
                stream.put(PdfName.FILTER, PdfName.DCTDECODE);
                stream.put(PdfName.WIDTH, new PdfNumber(width));
                stream.put(PdfName.HEIGHT, new PdfNumber(height));
                stream.put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
                stream.put(PdfName.COLORSPACE, PdfName.DEVICERGB);
            }
        }
        // Save altered PDF
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
        stamper.close();
        reader.close();
    }

    /**
     * Main method.
     *
     * @param    args    no arguments needed
     * @throws DocumentException 
     * @throws IOException
     */
    public static void main(String[] args) throws IOException, DocumentException {

        new ResizeImage().manipulatePdf(src, dest);
    }
}

Above Code reduces the size of image based on FACTOR. I don't want to reduce the dimension, but change DPI to reduce the image size. Any help will be appreciated. I am new to java. Also some other open source tools that can be used to compress the PDF?

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
undefined_variable
  • 6,180
  • 2
  • 22
  • 37

1 Answers1

3

you have copy/pasted an example from my book, but it seems that you have not read the book, nor have you really tried the example. You say "I don't want to reduce the dimension, but change DPI."

Well... that's exactly what the example in my book is doing! In the SpecialID example, I create a PDF with a page size defined using this rectangle: new Rectangle(400, 300). This means that we have a page that measures 400 by 300 points (see the blue dots in the screen shot below). To this page, I add a JPG of 500 by 332 pixels (see the red dots). This image is scaled to 400 by 300 points using the following method:

img.scaleAbsolute(400, 300);

This image takes 55332 bytes (green dot).

enter image description here

Note that we can easily calculate the DPI: the width of the image is 400 points; that is 5.555 inch. The height of the image is 300 points; that's 4.166 inch. The number of pixels for the width is 500, so the DPI in the X direction is 500 x 72 / 400 or 90 DPI. The number of pixels for the height is 332, so the DPI is 332 x 72 / 300 or 79.68 DPI.

You want to downsize the number of bytes of the JPG, by reducing the resolution. However: you want the size of the image to remain the same: it still has to cover 400 by 300 points.

This is exactly what is done in the ResizeImage example that you copy/pasted in your question. Let't take a look inside:

enter image description here

The image still measures 400 by 300 points (and that's what you want, isn't it?) but the resolution has dropped dramatically: the image is now 250 by 166 pixels instead of 500 by 332 pixels without changing its size on the page!

Now let's calculate the new DPIs: in X direction, we have 250 x 72 / 400. That's 45 DPI. In Y direction, we have 166 x 72 / 300. That's 39.84 DPI. That's exactly half the DPI we had before! Is that a coincidence? Of course not! That's the FACTOR that we used. (It's all a matter of simple Math.)

As a result of decreasing the resolution, the image now only takes 5343 bytes (instead of the original 55332). You have successfully compressed the image.

In short: you have misinterpreted the example you were using. You say that it doesn't achieve what you want. I can prove that it does ;-)

From the comments you posted on this answer, it appears that you are confusing concepts such as image resolution, image compression, etc. To take away that confusing, you should read the following questions and answers:

Community
  • 1
  • 1
Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • Thanks for you explanation. Because image still measures 400x300 and image is converted to 250x166 it is looking pixelated. Any means like downsampling or changing DPI value which will avoid pixelation. – undefined_variable Feb 06 '15 at 14:41
  • 1
    It seems that you don't understand the concept of "decreasing the resolution." – Bruno Lowagie Feb 06 '15 at 14:46
  • I got your point, but i need to avoid pixelation i am getting without dramatic increase in size. – undefined_variable Feb 06 '15 at 14:48
  • 1
    You need to change one or the other: either you want the image to be pixel perfect, in which case the size can be huge. Or you want the size to be low, in which case you'll reduce the quality of the image. It also seems to me that you don't understand the difference between image resolution and image compression. Your question is about reducing the resolution of a JPG. JPG is a lossy compression method for images. I shouldn't have to explain that if you had an education as a developer. – Bruno Lowagie Feb 06 '15 at 14:53
  • I'll update my answer with a couple of links you should read. – Bruno Lowagie Feb 06 '15 at 14:56
  • Thanks for your help.l know jpg is lossy compression technique. I have already compressed pdf using pdftron flattener and also ghostscript. For 100MB pdf both tool compressed pdf to 40MB. I was trying to achieve ySame. Thanks again – undefined_variable Feb 06 '15 at 18:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70413/discussion-between-undefined-variable-and-bruno-lowagie). – undefined_variable Feb 06 '15 at 18:08