aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/render/pdf
diff options
context:
space:
mode:
authorMehdi Houshmand <mehdi@apache.org>2012-06-20 13:31:51 +0000
committerMehdi Houshmand <mehdi@apache.org>2012-06-20 13:31:51 +0000
commitd32664f71c991d0d9fd4445d3fc1b5e83bb0a963 (patch)
tree4c97c07c0dbf5291f2ffb9f3e3cfe550c7dca180 /src/java/org/apache/fop/render/pdf
parent04c08fb9042bbc6d8183ba2787750c353c958c66 (diff)
parentda4e308bb1bc707814227c29d769f47da0bb3fb2 (diff)
downloadxmlgraphics-fop-d32664f71c991d0d9fd4445d3fc1b5e83bb0a963.tar.gz
xmlgraphics-fop-d32664f71c991d0d9fd4445d3fc1b5e83bb0a963.zip
Merged with trunk@1351540
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_URI_Unification@1352095 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/render/pdf')
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java69
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java258
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java73
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java65
4 files changed, 393 insertions, 72 deletions
diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
index 59d3930f7..36d58246f 100644
--- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -20,13 +20,16 @@
package org.apache.fop.render.pdf;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
+import java.awt.image.IndexColorModel;
+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.Image;
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
+import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDeviceColorSpace;
@@ -50,7 +53,9 @@ public abstract class AbstractImageAdapter implements PDFImage {
/** the image */
protected Image image;
- private PDFICCStream pdfICCStream = null;
+ private PDFICCStream pdfICCStream;
+
+ private static final int MAX_HIVAL = 255;
/**
* Creates a new PDFImage from an Image instance.
@@ -203,6 +208,68 @@ public abstract class AbstractImageAdapter implements PDFImage {
}
/**
+ * This is to be used by populateXObjectDictionary() when the image is palette based.
+ * @param dict the dictionary to fill in
+ * @param icm the image color model
+ */
+ protected void populateXObjectDictionaryForIndexColorModel(PDFDictionary dict, IndexColorModel icm) {
+ PDFArray indexed = new PDFArray(dict);
+ indexed.add(new PDFName("Indexed"));
+ if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
+ log.warn("Indexed color space is not using RGB as base color space."
+ + " The image may not be handled correctly." + " Base color space: "
+ + icm.getColorSpace() + " Image: " + image.getInfo());
+ }
+ indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ int c = icm.getMapSize();
+ int hival = c - 1;
+ if (hival > MAX_HIVAL) {
+ throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
+ }
+ indexed.add(Integer.valueOf(hival));
+ int[] palette = new int[c];
+ icm.getRGBs(palette);
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
+ for (int i = 0; i < c; i++) {
+ // TODO Probably doesn't work for non RGB based color spaces
+ // See log warning above
+ int entry = palette[i];
+ baout.write((entry & 0xFF0000) >> 16);
+ baout.write((entry & 0xFF00) >> 8);
+ baout.write(entry & 0xFF);
+ }
+ indexed.add(baout.toByteArray());
+
+ dict.put("ColorSpace", indexed);
+ dict.put("BitsPerComponent", icm.getPixelSize());
+
+ Integer index = getIndexOfFirstTransparentColorInPalette(icm);
+ if (index != null) {
+ PDFArray mask = new PDFArray(dict);
+ mask.add(index);
+ mask.add(index);
+ dict.put("Mask", mask);
+ }
+ }
+
+ private static Integer getIndexOfFirstTransparentColorInPalette(IndexColorModel icm) {
+ byte[] alphas = new byte[icm.getMapSize()];
+ byte[] reds = new byte[icm.getMapSize()];
+ byte[] greens = new byte[icm.getMapSize()];
+ byte[] blues = new byte[icm.getMapSize()];
+ icm.getAlphas(alphas);
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ for (int i = 0; i < icm.getMapSize(); i++) {
+ if ((alphas[i] & 0xFF) == 0) {
+ return Integer.valueOf(i);
+ }
+ }
+ return null;
+ }
+
+ /**
* Converts a ColorSpace object to a PDFColorSpace object.
* @param cs ColorSpace instance
* @return PDFColorSpace new converted object
diff --git a/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java
new file mode 100644
index 000000000..55264e2fe
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java
@@ -0,0 +1,258 @@
+/*
+ * 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.PDFICCStream;
+import org.apache.fop.pdf.PDFReference;
+
+public class ImageRawPNGAdapter extends AbstractImageAdapter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(ImageRawPNGAdapter.class);
+
+ private PDFICCStream pdfICCStream;
+ 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;
+
+ // 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.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 PDFICCStream getICCStream() {
+ return pdfICCStream;
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.PRECOMPRESSED_FILTER;
+ }
+
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ ColorModel cm = ((ImageRawPNG) image).getColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel) cm;
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
index c38a2edaf..b0b7b79d1 100644
--- a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
@@ -27,8 +27,6 @@ import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
-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;
@@ -36,14 +34,12 @@ import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.fop.pdf.AlphaRasterImage;
-import org.apache.fop.pdf.PDFArray;
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.PDFFilterList;
-import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFReference;
/**
@@ -162,30 +158,6 @@ public class ImageRenderedAdapter extends AbstractImageAdapter {
return (getImage().getTransparentColor() != null);
}
- private static Integer getIndexOfFirstTransparentColorInPalette(RenderedImage image) {
- ColorModel cm = image.getColorModel();
- if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- //Identify the transparent color in the palette
- byte[] alphas = new byte[icm.getMapSize()];
- byte[] reds = new byte[icm.getMapSize()];
- byte[] greens = new byte[icm.getMapSize()];
- byte[] blues = new byte[icm.getMapSize()];
- icm.getAlphas(alphas);
- icm.getReds(reds);
- icm.getGreens(greens);
- icm.getBlues(blues);
- for (int i = 0;
- i < ((IndexColorModel) cm).getMapSize();
- i++) {
- if ((alphas[i] & 0xFF) == 0) {
- return Integer.valueOf(i);
- }
- }
- }
- return null;
- }
-
/** {@inheritDoc} */
@Override
public PDFColor getTransparentColor() {
@@ -230,54 +202,13 @@ public class ImageRenderedAdapter extends AbstractImageAdapter {
}
}
- private static final int MAX_HIVAL = 255;
-
/** {@inheritDoc} */
@Override
public void populateXObjectDictionary(PDFDictionary dict) {
ColorModel cm = getEffectiveColorModel();
if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- PDFArray indexed = new PDFArray(dict);
- indexed.add(new PDFName("Indexed"));
-
- if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
- log.warn("Indexed color space is not using RGB as base color space."
- + " The image may not be handled correctly."
- + " Base color space: " + icm.getColorSpace()
- + " Image: " + image.getInfo());
- }
- indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
- int c = icm.getMapSize();
- int hival = c - 1;
- if (hival > MAX_HIVAL) {
- throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
- }
- indexed.add(Integer.valueOf(hival));
- int[] palette = new int[c];
- icm.getRGBs(palette);
- ByteArrayOutputStream baout = new ByteArrayOutputStream();
- for (int i = 0; i < c; i++) {
- //TODO Probably doesn't work for non RGB based color spaces
- //See log warning above
- int entry = palette[i];
- baout.write((entry & 0xFF0000) >> 16);
- baout.write((entry & 0xFF00) >> 8);
- baout.write(entry & 0xFF);
- }
- indexed.add(baout.toByteArray());
- IOUtils.closeQuietly(baout);
-
- dict.put("ColorSpace", indexed);
- dict.put("BitsPerComponent", icm.getPixelSize());
-
- Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage());
- if (index != null) {
- PDFArray mask = new PDFArray(dict);
- mask.add(index);
- mask.add(index);
- dict.put("Mask", mask);
- }
+ IndexColorModel icm = (IndexColorModel) cm;
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java
new file mode 100644
index 000000000..3475954a8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java
@@ -0,0 +1,65 @@
+/*
+ * 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 org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4)
+ * for PDF output.
+ */
+public class PDFImageHandlerRawPNG extends AbstractPDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
+
+ @Override
+ PDFImage createPDFImage(Image image, String xobjectKey) {
+ return new ImageRawPNGAdapter((ImageRawPNG) image, xobjectKey);
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 100;
+ }
+
+ /** {@inheritDoc} */
+ public Class<ImageRawPNG> getSupportedImageClass() {
+ return ImageRawPNG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ return (image == null || image instanceof ImageRawPNG)
+ && targetContext instanceof PDFRenderingContext;
+ }
+
+}