undefined_variable
undefined_variable

Reputation: 6218

How to compress PDF without resizing image?

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?

Upvotes: 0

Views: 4252

Answers (1)

Bruno Lowagie
Bruno Lowagie

Reputation: 77528

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:

Upvotes: 3

Related Questions