/*
* Copyright (C) 2014 Armin Häberling
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.aha.pdftools.transform;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfArray;
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.PdfStream;
import com.itextpdf.text.pdf.parser.PdfImageObject;
public class ShrinkImages extends PdfTransformation {
private static final int BITS_PER_COMPONENT = 8;
private double scaleFactor = 1.0;
public double getScaleFactor() {
return scaleFactor;
}
public void setScaleFactor(double scaleFactor) {
this.scaleFactor = scaleFactor;
}
@Override
protected void transformStream(PdfStream stream) throws IOException {
if (isImage(stream)) {
shrinkImage((PRStream) stream);
}
}
@Override
protected void postTransform(PdfReader reader) {
reader.removeUnusedObjects();
}
private void shrinkImage(PRStream stream) throws IOException {
PdfObject colorSpace = stream.get(PdfName.COLORSPACE);
PdfNumber bitsPerComponent = (PdfNumber) stream.get(PdfName.BITSPERCOMPONENT);
if (!isColorSpaceSupported(colorSpace, bitsPerComponent)) {
return;
}
PdfImageObject image = new PdfImageObject(stream);
BufferedImage origImg = image.getBufferedImage();
BufferedImage img = resize(origImg);
byte[] imgData = writeAsJpeg(img);
replaceImage(stream, imgData, img.getWidth(), img.getHeight(), colorSpace);
}
private boolean isColorSpaceSupported(PdfObject colorSpace, PdfNumber bitsPerComponent) {
if (bitsPerComponent.intValue() != BITS_PER_COMPONENT) {
return false;
}
if (colorSpace.isArray() && ((PdfArray) colorSpace).contains(PdfName.INDEXED)) {
return false;
}
return true;
}
private void replaceImage(PRStream stream, byte[] imgData, int width, int height, PdfObject colorSpace) {
stream.clear();
stream.setData(imgData);
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(BITS_PER_COMPONENT));
stream.put(PdfName.COLORSPACE, colorSpace);
}
private byte[] writeAsJpeg(BufferedImage img) throws IOException {
ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write(img, "JPG", imgBytes);
return imgBytes.toByteArray();
}
private BufferedImage resize(BufferedImage origImg) {
int width = (int) (origImg.getWidth() * scaleFactor);
int height = (int) (origImg.getHeight() * scaleFactor);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
AffineTransform at = AffineTransform.getScaleInstance(scaleFactor, scaleFactor);
g.drawRenderedImage(origImg, at);
return img;
}
private boolean isImage(PdfStream stream) {
return PdfName.IMAGE.equals(stream.get(PdfName.SUBTYPE));
}
}