Compressing images in existing pdfs makes the resu

2019-09-09 00:04发布

问题:

Im having a problem with image compression. I used the answer described in this question compress pdf with large images via java if i set the FACTOR variable to 0.9f or 1f (original size) the resulting pdf file starts to get bigger than the ORIGINAL. But that is not the case for all files. Some files created by myself are getting smaller like planned but some just get bigger like +1/3rd and i get black backgrounds on some images ontop of it. this is getting even worse when im using the normal image compression without resizing the image This is my test file.

Lowagies method: (resize the images)

    // TODO Auto-generated method stub
    PdfName key = new PdfName("ITXT_SpecialId");
    PdfName value = new PdfName("123456789");
    // Read the file
    PdfReader reader = new PdfReader(args[0]);
    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))) {
        PdfObject pdfsubtype = stream.get(PdfName.SUBTYPE);
        System.out.println(stream.type());
        if (pdfsubtype != null && pdfsubtype.toString().equals(PdfName.IMAGE.toString())) {
            PdfImageObject image = new PdfImageObject(stream);
            BufferedImage bi = image.getBufferedImage();
            if (bi == null) continue;
            int width = (int)(bi.getWidth() * 1f);
            int height = (int)(bi.getHeight() * 1f);
            BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            AffineTransform at = AffineTransform.getScaleInstance(1f, 1f);
            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.BEST_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("/Applications/XAMPP/xamppfiles/htdocs/pdf_compress/download/"+args[2]));
    stamper.close();
    reader.close();

My method (Using real compression by setting the quallity of the image instead of resizing it)

        PdfReader reader = new PdfReader(args[0]);

        // Read the file
        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;


            PdfObject pdfsubtype = stream.get(PdfName.SUBTYPE);
            if (pdfsubtype != null && pdfsubtype.toString().equals(PdfName.IMAGE.toString())) {


                System.out.println(pdfsubtype.length());
                PdfImageObject image = new PdfImageObject(stream);

                BufferedImage bi = image.getBufferedImage();


                if (bi == null) continue;
                int width = (int)(bi.getWidth());
                int height = (int)(bi.getHeight());


                if(width <=30 || height <=30){
                    continue;

                }
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                AffineTransform at = null;
                Graphics2D g = img.createGraphics();
                g.drawRenderedImage(bi, at );
                ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
                Iterator iter = ImageIO.getImageWritersByFormatName("JPG");
                ImageWriter writer = (ImageWriter)iter.next();
                ImageWriteParam iwp = writer.getDefaultWriteParam();
                iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// here goes the compression
                iwp.setCompressionQuality(Float.valueOf(args[1]));
                ImageOutputStream imageos = ImageIO.createImageOutputStream(imgBytes);
                writer.setOutput(imageos);
                IIOImage images = new IIOImage(img, null, null);

                writer.write(null,images , iwp);
                imageos.close();
                writer.dispose();

                stream.clear();
                stream.setData(imgBytes.toByteArray(), false, PRStream.BEST_COMPRESSION);
                stream.put(PdfName.TYPE, PdfName.XOBJECT);
                stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
                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);
            }
        }           
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("/Applications/XAMPP/xamppfiles/htdocs/pdf_compress/download/"+args[2]));
        stamper.setFullCompression();

        stamper.close();
        reader.close();
        System.out.println("Done");

What is wrong with the code? Should i use a different image compression method? Are there any others?

回答1:

When I only replace JPEGs, I already get a lower file size. Removing the unused object also helps:

public class ReduceSize {

    public static final String SRC = "resources/pdfs/annual_report_2009.pdf";
    public static final String DEST = "results/images/annual_report_2009.pdf";
    public static final float FACTOR = 0.5f;

    public static void main(String[] args) throws DocumentException, IOException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new ReduceSize().manipulatePdf(SRC, DEST);
    }
    public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
        PdfReader reader = new PdfReader(src);
        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 (!PdfName.IMAGE.equals(stream.getAsName(PdfName.SUBTYPE)))
                continue;
            if (!PdfName.DCTDECODE.equals(stream.getAsName(PdfName.FILTER)))
                continue;
            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);
            if (width <= 0 || height <= 0)
                continue;
            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(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);
        }
        reader.removeUnusedObjects();
        // Save altered PDF
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        stamper.setFullCompression();
        stamper.close();
        reader.close();
    }
}

This reduces the 10,510 KB file to 9,159 KB. Of course: fonts also take up quite some space.