org.apache.fop.render.pdf.ImageRawPNGAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.fop Show documentation
Show all versions of org.apache.fop Show documentation
The core maven build properties
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
// Original author: Matthias Reichenbacher
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 {
/** logging instance */
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;
/**
* Creates a new PDFImage from an Image instance.
* @param image the image
* @param key XObject key
*/
public ImageRawPNGAdapter(ImageRawPNG image, String key) {
super(image, key);
}
/** {@inheritDoc} */
public void setup(PDFDocument doc) {
super.setup(doc);
ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
if (cm instanceof IndexColorModel) {
numberOfInterleavedComponents = 1;
} else {
// this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
// numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
numberOfInterleavedComponents = cm.getNumComponents();
}
// set up image compression for non-alpha channel
FlateFilter flate;
try {
flate = new FlateFilter();
flate.setApplied(true);
flate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
if (numberOfInterleavedComponents < 3) {
// means palette (1) or gray (1) or gray + alpha (2)
flate.setColors(1);
} else {
// means rgb (3) or rgb + alpha (4)
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();
// Handle transparency channel if applicable; note that for palette images the transparency is
// not TRANSLUCENT
if (cm.hasAlpha() && cm.getTransparency() == ColorModel.TRANSLUCENT) {
doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());
// TODO: Implement code to combine image with background color if transparency is not allowed
// here we need to inflate the PNG pixel data, which includes alpha, separate the alpha channel
// and then deflate it back again
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);
// offset is the byte offset of the alpha component
int offset = numberOfInterleavedComponents - 1; // 1 for GA, 3 for RGBA
int numColumns = image.getSize().getWidthPx();
int bytesPerRow = numberOfInterleavedComponents * numColumns;
int filter;
// read line by line; the first byte holds the 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);
}
// set up alpha channel compression
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();
}
}
/** {@inheritDoc} */
public PDFDeviceColorSpace getColorSpace() {
// DeviceGray, DeviceRGB, or DeviceCMYK
return toPDFColorSpace(image.getColorSpace());
}
/** {@inheritDoc} */
public int getBitsPerComponent() {
return ((ImageRawPNG) this.image).getBitDepth();
}
/** {@inheritDoc} */
public boolean isTransparent() {
return ((ImageRawPNG) this.image).isTransparent();
}
/** {@inheritDoc} */
public PDFColor getTransparentColor() {
return new PDFColor(((ImageRawPNG) this.image).getTransparentColor());
}
/** {@inheritDoc} */
public String getMask() {
return maskRef;
}
/** {@inheritDoc} */
public String getSoftMask() {
return softMask.toString();
}
/** {@inheritDoc} */
public PDFReference getSoftMaskReference() {
return softMask;
}
/** {@inheritDoc} */
public PDFFilter getPDFFilter() {
return pdfFilter;
}
/** {@inheritDoc} */
public void outputContents(OutputStream out) throws IOException {
InputStream in = ((ImageRawStream) image).createInputStream();
try {
if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
// means we have Gray, RGB, or Palette
IOUtils.copy(in, out);
} else {
// means we have Gray + alpha or RGB + alpha
// TODO: since we have alpha here do this when the alpha channel is extracted
int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
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;
// here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
// channel and then deflate the RGB channels back again
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);
}
}
/** {@inheritDoc} */
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:
// ignore
}
}
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy