iText: уменьшить качество изображения (для уменьшения результирующего размера PDF)



Как лучше всего уменьшить размер изображений JPEG в PDF-файле, созданном с использованием iText? (Моя цель-найти компромисс между качеством изображения и размером файла.)



Образы создаются следующим образом:



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


Я хотел бы предоставить масштабный коэффициент, например 0.5, который вдвое уменьшает количество пикселей в строке.



Скажем, я создаю PDF с одним изображением размером 3 МБ. Я попробовал image.scale(0.5f, 0.5f), но полученный PDF-файл по-прежнему составляет примерно 3 МБ. Я ожидал этого. станьте намного меньше.



Таким образом, я предполагаю, что исходное изображение, встроенное в PDF-файл, не трогается. Но это то, что мне нужно: общее количество пикселей во всем PDF-файле, хранящемся на диске, должно быть уменьшено.



Каков самый простой / рекомендуемый способ достичь этого?

645   3  

3 ответов:

Сначала масштабируйте изображение, а затем откройте масштабируемое изображение с помощью iText.

В ImageDataFactory есть метод create, который принимает изображение AWT. Сначала масштабируйте изображение с помощью инструментов AWT, а затем откройте его следующим образом:

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

Более подробные советы по масштабированию изображения см. В разделе Как изменить размер изображения с помощью Java?

Если вам все еще нужен исходный размер при добавлении в PDF, просто масштабируйте его снова.

itextImage.scale(2f, 2f);

Примечание: этот код непроверенный.


EDIT в ответ на комментарии к bounty

Вы заставили меня задуматься и искать. Похоже, iText рассматривает импорт изображения AWT как необработанное изображение. Я предполагаю, что он обрабатывает его так же, как BMP, который просто записывает пиксельные данные с помощью /FlateDecode, что, вероятно, значительно меньше оптимального. Единственный способ, который я могу придумать, чтобы выполнить ваше требование, - это использовать ImageIO для записи масштабированного изображения в файловую систему или ByteArrayOutputStream в формате jpeg, затем используйте полученный файл / байты для открытия с помощью iText.

Вот обновленный пример использования байтовых массивов. Если вы хотите получить еще больше фантазии с уровнями сжатия и так далее, смотрите здесь .

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

Вы можете установить уровень сжатия объекта writer, какой хотите.

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

Если уровень сжатия равен 0, то изображение, отображаемое в pdf, не будет принимать никакого размера. Размер Pdf с изображением и без изображения будет одинаковым. Тем не менее масштабирование также возможно, независимо от высоты и ширины, которую вы хотите.

Существует способ, который перечислен в этой документации, что дает вам доступ к сжатию изображения и сокращению всего PDF-файла, хранящегося на диске. надеюсь, это поможет.

Ниже для примера кода:

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

Comments

    Ничего не найдено.