IText: reduce image quality (to reduce the resulting PDF size)

What is the best practice for reducing the size of JPEG images in a PDF file created using iText ? (My goal is a compromise between image quality and file size.)

Images are created as follows:

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

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

Let's say I create a PDF file with one 3 megabyte image. I tried image.scale(0.5f, 0.5f) , but the resulting PDF file is still approximately 3 MB. I expected it to become much smaller.

Thus, I think the original image embedded in the PDF is not affected. But this 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?

+5
source share
3 answers

First scale the image, then open the scaled image using iText.

ImageDataFactory has a create method that accepts an AWT image. First scale the image using AWT tools, then open it as follows:

 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 more information on image scaling, see How do I resize an image using Java?

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

 itextImage.scale(2f, 2f); 

Note. This code has not been verified.


EDIT in response to award comments

You made me think and watch. It seems iText is considering importing AWT images as a raw image. I guess it treats it the same way as BMP, which simply writes pixel data using / FlateDecode , which is probably much less than optimal. The only thing I can think of to achieve your requirement is to use ImageIO to write a scaled image to the file system or ByteArrayOutputStream as jpeg, and then use the resulting file / bytes to open with iText.

Here is an example using byte arrays. If you want to know more about compression levels, etc., see 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)); 
+3
source

In these > documents, there is a way that gives you access to image compression and reduction of the entire PDF file stored on disk. hope this helps.

The following is sample code:

 /* * This example was written by Bruno Lowagie in answer to the following question: * http://stackoverflow.com/questions/30483622/compressing-images-in-existing-pdfs-makes-the-resulting-pdf-file-bigger-lowagie */ package sandbox.images; 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; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import javax.imageio.ImageIO; import sandbox.WrapToTest; /** * @author Bruno Lowagie (iText Software) */ @WrapToTest public class ReduceSize { public static final String SRC = "resources/pdfs/single_image.pdf"; public static final String DEST = "results/images/single_image_reduced.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(); } } 
0
source

You can set the compression level of the recording object as you like.

 PdfWriter writer = new PdfWriter("MyPdf.pdf"); writer.setCompressionLevel(0); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf, PageSize.A4); 

if the compression level is 0, then the image displayed in pdf format will not have any size. The pdf size with and without image will be the same. Scaling is also possible, regardless of height and width.

0
source

Source: https://habr.com/ru/post/1275963/


All Articles