【问题标题】:Compressing images in existing pdfs makes the resulting PDF file bigger (Lowagies resizing method and real compression method)压缩现有 pdf 中的图像会使生成的 PDF 文件更大(Lowagies 调整大小方法和实际压缩方法)
【发布时间】:2015-08-09 14:34:40
【问题描述】:

我遇到了图像压缩问题。我使用了这个问题中描述的答案 compress pdf with large images via java 如果我将 FACTOR 变量设置为 0.9f 或 1f(原始大小),则生成的 pdf 文件开始变得比原始文件大。但并非所有文件都如此。我自己创建的一些文件按计划变小了,但有些文件变大了,比如 +1/3rd,我在上面的一些图像上得到黑色背景。当我使用普通图像压缩而不调整图像大小时,情况会变得更糟 This 是我的测试文件。

Lowagies 方法:(调整图像大小)

    // 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();

我的方法(通过设置图像的质量而不是调整图像大小来使用真正的压缩)

        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");

代码有什么问题?我应该使用不同的图像压缩方法吗?还有其他的吗?

【问题讨论】:

  • A bad workman always blames Lowagie ;-) 您正在将所有图像转换为 JPEG(这就是 DCTDECODE)。您肯定了解某些图像类型更佳。为什么不首先检查图像的类型,确保只降低 JPEG 的分辨率,而保持其他类型的图像完好无损?
  • 例如:在第 7 页(或 C5,如果您愿意),您有很多只有几个像素的小图像。您不应该将它们转换为 JPEG。它们已经尽可能小了。
  • 线程“主”javax.imageio.IIOException 中的异常:com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1043) 中 com.sun.imageio 中不支持的图像类型.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1014) 在 javax.imageio.ImageIO.read(ImageIO.java:1422) 在 javax.imageio.ImageIO.read(ImageIO.java:1326) 在 com.itextpdf。 Classes.Test.main(Test.java:59) 中的 text.pdf.parser.PdfImageObject.getBufferedImage(PdfImageObject.java:405) 我在 image.getBufferedImage 的行中得到了它。 (不同的文件)
  • 它是一个带有 CMYK jpeg 颜色空间的文件
  • 这不是 iText 问题,是吗? JPEG 按原样嵌入在 PDF 中。 iText 没有改变一个字节。不是在创建 PDF 时,不是在您提取字节时。当您查看堆栈跟踪时,您会发现这是一个纯 Java 问题:imageio 类不支持这样的 PDF,因此您必须找到支持的成像类。

标签: java image pdf compression itext


【解决方案1】:

当我只替换 JPEG 时,我已经获得了较小的文件大小。删除未使用的对象也有帮助:

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();
    }
}

这会将 10,510 KB 的文件减少到 9,159 KB。当然:字体也占用不少空间。

【讨论】:

  • 字体是这个文档占用了 2% 的空间(根据 Acrobat),所以没有什么好处。图像数据中大约有 30%,“内容流”中大约有 50%。这是令人惊讶的高,但我没有更详细地查看那是什么......
  • 好吧,我没仔细看字体。当我在文本编辑器中滚动浏览文档时,我确实看到了很多 XML,并且有很多“像素”大小的图像。
  • 是的,有一堆 XMP 元数据可以被删除以获得一些空间......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-18
  • 2014-01-04
  • 2021-12-01
  • 2018-02-25
  • 2020-01-05
相关资源
最近更新 更多