package org.apache.fop.render.pdf;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
import org.apache.fop.pdf.BitmapImage;
import org.apache.fop.pdf.FlateFilter;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFFilterException;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFReference;
public class ImageRawPNGAdapter extends AbstractImageAdapter {
private static Log log = LogFactory.getLog(ImageRawPNGAdapter.class);
private static final PDFName RI_PERCEPTUAL = new PDFName("Perceptual");
private static final PDFName RI_RELATIVE_COLORIMETRIC = new PDFName("RelativeColorimetric");
private static final PDFName RI_SATURATION = new PDFName("Saturation");
private static final PDFName RI_ABSOLUTE_COLORIMETRIC = new PDFName("AbsoluteColorimetric");
private PDFFilter pdfFilter;
private String maskRef;
private PDFReference softMask;
private int numberOfInterleavedComponents;
public ImageRawPNGAdapter(ImageRawPNG image, String key) {
super(image, key);
}
public void setup(PDFDocument doc) {
super.setup(doc);
ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
if (cm instanceof IndexColorModel) {
numberOfInterleavedComponents = 1;
} else {
numberOfInterleavedComponents = cm.getNumComponents();
}
FlateFilter flate;
try {
flate = new FlateFilter();
flate.setApplied(true);
flate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
if (numberOfInterleavedComponents < 3) {
flate.setColors(1);
} else {
flate.setColors(3);
}
flate.setColumns(image.getSize().getWidthPx());
flate.setBitsPerComponent(this.getBitsPerComponent());
} catch (PDFFilterException e) {
throw new RuntimeException("FlateFilter configuration error", e);
}
this.pdfFilter = flate;
this.disallowMultipleFilters();
if (cm.hasAlpha() && cm.getTransparency() == ColorModel.TRANSLUCENT) {
doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
InputStream in = ((ImageRawStream) image).createInputStream();
try {
InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
DataInputStream dataStream = new DataInputStream(infStream);
int offset = numberOfInterleavedComponents - 1;
int numColumns = image.getSize().getWidthPx();
int bytesPerRow = numberOfInterleavedComponents * numColumns;
int filter;
while ((filter = dataStream.read()) != -1) {
byte[] bytes = new byte[bytesPerRow];
dataStream.readFully(bytes, 0, bytesPerRow);
dos.write((byte) filter);
for (int j = 0; j < numColumns; j++) {
dos.write(bytes, offset, 1);
offset += numberOfInterleavedComponents;
}
offset = numberOfInterleavedComponents - 1;
}
dos.close();
} catch (IOException e) {
throw new RuntimeException("Error processing transparency channel:", e);
} finally {
IOUtils.closeQuietly(in);
}
FlateFilter transFlate;
try {
transFlate = new FlateFilter();
transFlate.setApplied(true);
transFlate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
transFlate.setColors(1);
transFlate.setColumns(image.getSize().getWidthPx());
transFlate.setBitsPerComponent(this.getBitsPerComponent());
} catch (PDFFilterException e) {
throw new RuntimeException("FlateFilter configuration error", e);
}
BitmapImage alphaMask = new BitmapImage("Mask:" + this.getKey(), image.getSize().getWidthPx(),
image.getSize().getHeightPx(), baos.toByteArray(), null);
alphaMask.setPDFFilter(transFlate);
alphaMask.disallowMultipleFilters();
alphaMask.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
softMask = doc.addImage(null, alphaMask).makeReference();
}
}
public PDFDeviceColorSpace getColorSpace() {
return toPDFColorSpace(image.getColorSpace());
}
public int getBitsPerComponent() {
return ((ImageRawPNG) this.image).getBitDepth();
}
public boolean isTransparent() {
return ((ImageRawPNG) this.image).isTransparent();
}
public PDFColor getTransparentColor() {
return new PDFColor(((ImageRawPNG) this.image).getTransparentColor());
}
public String getMask() {
return maskRef;
}
public String getSoftMask() {
return softMask.toString();
}
public PDFReference getSoftMaskReference() {
return softMask;
}
public PDFFilter getPDFFilter() {
return pdfFilter;
}
public void outputContents(OutputStream out) throws IOException {
InputStream in = ((ImageRawStream) image).createInputStream();
try {
if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
IOUtils.copy(in, out);
} else {
int numBytes = numberOfInterleavedComponents - 1;
int numColumns = image.getSize().getWidthPx();
InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
DataInputStream dataStream = new DataInputStream(infStream);
int offset = 0;
int bytesPerRow = numberOfInterleavedComponents * numColumns;
int filter;
DeflaterOutputStream dos = new DeflaterOutputStream(out, new Deflater());
while ((filter = dataStream.read()) != -1) {
byte[] bytes = new byte[bytesPerRow];
dataStream.readFully(bytes, 0, bytesPerRow);
dos.write((byte) filter);
for (int j = 0; j < numColumns; j++) {
dos.write(bytes, offset, numBytes);
offset += numberOfInterleavedComponents;
}
offset = 0;
}
dos.close();
}
} finally {
IOUtils.closeQuietly(in);
}
}
public String getFilterHint() {
return PDFFilterList.PRECOMPRESSED_FILTER;
}
public void populateXObjectDictionary(PDFDictionary dict) {
int renderingIntent = ((ImageRawPNG) image).getRenderingIntent();
if (renderingIntent != -1) {
switch (renderingIntent) {
case 0:
dict.put("Intent", RI_PERCEPTUAL);
break;
case 1:
dict.put("Intent", RI_RELATIVE_COLORIMETRIC);
break;
case 2:
dict.put("Intent", RI_SATURATION);
break;
case 3:
dict.put("Intent", RI_ABSOLUTE_COLORIMETRIC);
break;
default:
}
}
ColorModel cm = ((ImageRawPNG) image).getColorModel();
if (cm instanceof IndexColorModel) {
IndexColorModel icm = (IndexColorModel) cm;
super.populateXObjectDictionaryForIndexColorModel(dict, icm);
}
}
protected boolean issRGB() {
if (((ImageRawPNG) image).getRenderingIntent() != -1) {
return true;
}
return false;
}
}