Author: lehmi
Date: Sun Oct  7 13:05:16 2012
New Revision: 1395294

URL: http://svn.apache.org/viewvc?rev=1395294&view=rev
Log:
PDFBOX-1169: added YCCK2RGB conversion for JPEGs using a CYMK colorspace

Modified:
    
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java
URL: 
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java?rev=1395294&r1=1395293&r2=1395294&view=diff
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java
 (original)
+++ 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDJpeg.java
 Sun Oct  7 13:05:16 2012
@@ -40,6 +40,7 @@ import java.util.List;
 import javax.imageio.ImageIO;
 import javax.imageio.IIOException;
 import javax.imageio.ImageReader;
+import javax.imageio.metadata.IIOMetadata;
 import javax.imageio.stream.ImageInputStream;
 
 import org.apache.pdfbox.cos.COSArray;
@@ -57,6 +58,9 @@ import org.apache.pdfbox.pdmodel.graphic
 import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased;
 import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation;
 import org.apache.pdfbox.util.ImageIOUtil;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * An image class for JPegs.
@@ -226,9 +230,21 @@ public class PDJpeg extends PDXObjectIma
             if (cs instanceof PDDeviceCMYK 
                     || (cs instanceof PDICCBased && cs.getNumberOfComponents() 
== 4))
             {
+                // JPEGs may contain CMYK, YCbCr or YCCK decoded image data
+                int transform = getApp14AdobeTransform(img);
                 // create BufferedImage based on the converted color values
-                bi = convertCMYK2RGB(readImage(img), cs);
-
+                if (transform == 0)
+                {
+                    bi = convertCMYK2RGB(readImage(img), cs);
+                }
+                else if (transform == 1)
+                {
+                    // TODO YCbCr
+                }
+                else if (transform == 2)
+                {
+                    bi = convertYCCK2RGB(readImage(img));
+                }
             }
             else if (cs instanceof PDSeparation)
             {
@@ -340,24 +356,77 @@ public class PDJpeg extends PDXObjectIma
         return newImage;
     }
 
+    private int getApp14AdobeTransform(byte[] bytes) 
+    {
+        int transformType = 0;
+        ImageReader reader = null;
+        ImageInputStream input = null;
+        try 
+        {
+            input = ImageIO.createImageInputStream(new 
ByteArrayInputStream(bytes));
+            Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
+            if (readers == null || !readers.hasNext()) 
+            {
+                input.close();
+                throw new RuntimeException("No ImageReaders found");
+            }
+            reader = (ImageReader) readers.next();
+            reader.setInput(input);
+            IIOMetadata meta = reader.getImageMetadata(0);
+            if (meta != null)
+            {
+                Node tree = meta.getAsTree("javax_imageio_jpeg_image_1.0");
+                NodeList children = tree.getChildNodes();
+                for (int i=0;i<children.getLength();i++)
+                {
+                    Node markerSequence = children.item(i);
+                    if ("markerSequence".equals(markerSequence.getNodeName()))
+                    {
+                        NodeList markerSequenceChildren = 
markerSequence.getChildNodes();
+                        for (int j=0;j<markerSequenceChildren.getLength();j++)
+                        {
+                            Node child = markerSequenceChildren.item(j);
+                            if ("app14Adobe".equals(child.getNodeName()) && 
child.hasAttributes())
+                            {
+                                NamedNodeMap attribs = child.getAttributes();
+                                Node transformNode = 
attribs.getNamedItem("transform");
+                                transformType = 
Integer.parseInt(transformNode.getNodeValue());
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        catch(IOException exception)
+        {
+            
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                reader.dispose();
+            }
+        }
+        return transformType;
+    }
+    
     private Raster readImage(byte[] bytes) throws IOException 
     {
         ImageInputStream input = ImageIO.createImageInputStream(new 
ByteArrayInputStream(bytes));
         Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
         if (readers == null || !readers.hasNext()) 
         {
+            input.close();
             throw new RuntimeException("No ImageReaders found");
         }
-
         // read the raster information only
         // avoid to access the meta information
         ImageReader reader = (ImageReader) readers.next();
         reader.setInput(input);
         Raster raster = reader.readRaster(0, reader.getDefaultReadParam());
-        if (input != null) 
-        {
-            input.close();
-        }
+        input.close();
         reader.dispose();
         return raster;
     }
@@ -392,18 +461,39 @@ public class PDJpeg extends PDXObjectIma
                 rgbIndex +=3;
             }
         }
-        // create a RGB color model
-        ColorModel cm = new 
ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), 
-                false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
-        // create the target raster
-        WritableRaster writeableRaster = 
cm.createCompatibleWritableRaster(width, height);
-        // get the data buffer of the raster
-        DataBufferByte buffer = 
(DataBufferByte)writeableRaster.getDataBuffer();
-        byte[] bufferData = buffer.getData();
-        // copy all the converted data to the raster buffer
-        System.arraycopy( rgb, 0,bufferData, 0,rgb.length );
-        // create an image using the converted color values
-        return new BufferedImage(cm, writeableRaster, true, null);
+        return 
createRGBBufferedImage(ColorSpace.getInstance(ColorSpace.CS_sRGB), rgb, width, 
height);
+    }
+
+    // YCbCrK jpegs are not supported by JAI, so that we have to do the 
conversion on our own
+    private BufferedImage convertYCCK2RGB(Raster raster) throws IOException 
+    {
+        int width = raster.getWidth();
+        int height = raster.getHeight();
+        byte[] rgb = new byte[width * height * 3]; 
+        int rgbIndex = 0;
+        for (int i = 0; i < height; i++) 
+        {
+            for (int j = 0; j < width; j++)
+            {
+                float[] srcColorValues = raster.getPixel(j,i, (float[])null);
+                float k = srcColorValues[3];
+                float y = srcColorValues[0];
+                float c1=srcColorValues[1];
+                float c2=srcColorValues[2];
+
+                double val = y + 1.402 * ( c2 - 128 ) - k;
+                rgb[rgbIndex] = val < 0.0 ? (byte)0 : (val>255.0? (byte)0xff : 
(byte)(val+0.5));
+                
+                val = y - 0.34414 * ( c1 - 128 ) - 0.71414 * ( c2 - 128 ) - k;
+                rgb[rgbIndex+1] = val<0.0? (byte)0: val>255.0? (byte)0xff: 
(byte)(val+0.5);
+
+                val = y + 1.772 * ( c1 - 128 ) - k;
+                rgb[rgbIndex+2] = val<0.0? (byte)0: val>255.0? (byte)0xff: 
(byte)(val+0.5);
+                
+                rgbIndex +=3;
+            }
+        }
+        return 
createRGBBufferedImage(ColorSpace.getInstance(ColorSpace.CS_sRGB), rgb, width, 
height);
     }
 
     // Separation and DeviceN colorspaces are using a tint transform function 
to convert color values 
@@ -414,7 +504,7 @@ public class PDJpeg extends PDXObjectIma
         int numberOfOutputValues = function.getNumberOfOutputParameters();
         int width = raster.getWidth();
         int height = raster.getHeight();
-        byte[] sourceBuffer = new byte[width * height * numberOfOutputValues]; 
+        byte[] rgb = new byte[width * height * numberOfOutputValues]; 
         int bufferIndex = 0;
         for (int i = 0; i < height; i++) 
         {
@@ -432,24 +522,28 @@ public class PDJpeg extends PDXObjectIma
                 // convert values from 0..1 to 0..255
                 for (int k = 0; k < numberOfOutputValues; k++)
                 {
-                    sourceBuffer[bufferIndex+k] = (byte)(convertedValues[k] * 
255); 
+                    rgb[bufferIndex+k] = (byte)(convertedValues[k] * 255); 
                 }
                 bufferIndex +=numberOfOutputValues;
             }
         }
-        // create a target color model
-        ColorModel cm = new ComponentColorModel(colorspace, 
-                false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+        return createRGBBufferedImage(colorspace, rgb, width, height);
+    }
+
+    private BufferedImage createRGBBufferedImage(ColorSpace cs, byte[] rgb, 
int width, int height) 
+    {
+        // create a RGB color model
+        ColorModel cm = new ComponentColorModel(cs, false, false, 
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
         // create the target raster
         WritableRaster writeableRaster = 
cm.createCompatibleWritableRaster(width, height);
         // get the data buffer of the raster
         DataBufferByte buffer = 
(DataBufferByte)writeableRaster.getDataBuffer();
         byte[] bufferData = buffer.getData();
         // copy all the converted data to the raster buffer
-        System.arraycopy( sourceBuffer, 0,bufferData, 0,sourceBuffer.length );
+        System.arraycopy( rgb, 0,bufferData, 0,rgb.length );
         // create an image using the converted color values
         return new BufferedImage(cm, writeableRaster, true, null);
     }
-
+    
 }
 


Reply via email to