]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugfix for ICC color profile parsing in JPEG images.
authorJeremias Maerki <jeremias@apache.org>
Fri, 24 Mar 2006 08:56:55 +0000 (08:56 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 24 Mar 2006 08:56:55 +0000 (08:56 +0000)
Ported detection code for CMYK JPEG images generated by Adobe Photoshop from maintenance branch. Adobe inverts CMYK images.
Reenabled ICC color profile embedding for PDF.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@388460 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/image/AbstractFopImage.java
src/java/org/apache/fop/image/FopImage.java
src/java/org/apache/fop/image/JpegImage.java
src/java/org/apache/fop/pdf/BitmapImage.java
src/java/org/apache/fop/pdf/PDFImage.java
src/java/org/apache/fop/pdf/PDFXObject.java
src/java/org/apache/fop/render/pdf/FopPDFImage.java
src/java/org/apache/fop/render/ps/PSGraphics2D.java

index af45930383a2ce0fd628cb4a5f35fa062cb0a846..b6cfeba3f458fda5e6f0bee2108a526445a25721 100644 (file)
@@ -103,6 +103,11 @@ public abstract class AbstractFopImage implements FopImage {
      */
     protected Color transparentColor = null;
 
+    /**
+     * Photoshop generated CMYK JPEGs are inverted.
+     */
+     protected boolean invertImage = false;
+    
     /**
      * Constructor.
      * Construct a new FopImage object and initialize its default properties:
@@ -321,6 +326,11 @@ public abstract class AbstractFopImage implements FopImage {
         return this.transparentColor;
     }
 
+    /** @return true for CMYK images generated by Adobe Photoshop */
+    public boolean isInverted() {
+        return this.invertImage;
+    }
+    
     /**
      * Return the image data (pixels, uncompressed).
      * @return the image data
index 510a66f0586bca6c26fb75c3416fcced7ee15028..60e63771863bca283649b21494325597474e145d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -141,6 +141,9 @@ public interface FopImage {
      */
     byte[] getSoftMask();
 
+    /** @return true for CMYK images generated by Adobe Photoshop */
+    boolean isInverted();
+    
     /**
      * Returns the decoded and uncompressed image as a array of
      * width * height * [colorspace-multiplicator] pixels.
index ad4f6fe408eb2e4a4c285bc2d094e3d3733e8cc1..247322d184143aae4fffde75e1f0528be3fad390 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 package org.apache.fop.image;
 
 // Java
-import java.io.ByteArrayOutputStream;
 import java.awt.color.ColorSpace;
 import java.awt.color.ICC_Profile;
 
 // FOP
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.fop.util.CMYKColorSpace;
 
 /**
@@ -36,6 +36,7 @@ import org.apache.fop.util.CMYKColorSpace;
 public class JpegImage extends AbstractFopImage {
     private ICC_Profile iccProfile = null;
     private boolean foundICCProfile = false;
+    private boolean hasAPPEMarker = false;
 
     /**
      * Create a jpeg image with the info.
@@ -55,7 +56,7 @@ public class JpegImage extends AbstractFopImage {
      */
     protected boolean loadOriginalData() {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ByteArrayOutputStream iccStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream iccStream = null;
         int index = 0;
         boolean cont = true;
 
@@ -136,11 +137,29 @@ public class JpegImage extends AbstractFopImage {
                                               this.raw[index + 2],
                                               this.raw[index + 3]) + 2;
 
+                            if (iccStream == null) {
+                                iccStream = new ByteArrayOutputStream();
+                            }
                             iccStream.write(this.raw,
                                             index + 18, chunkSize - 18);
 
                         }
 
+                        index += calcBytes(this.raw[index + 2],
+                                           this.raw[index + 3]) + 2;
+                    // Check for Adobe APPE Marker
+                    } else if ((uByte(this.raw[index]) == 0xff
+                                && uByte(this.raw[index + 1]) == 0xee
+                                && uByte(this.raw[index + 2]) == 0
+                                && uByte(this.raw[index + 3]) == 14
+                                && "Adobe".equals(new String(this.raw, index + 4, 5)))) {
+                        // The reason for reading the APPE marker is that Adobe Photoshop
+                        // generates CMYK JPEGs with inverted values. The correct thing
+                        // to do would be to interpret the values in the marker, but for now
+                        // only assume that if APPE marker is present and colorspace is CMYK,
+                        // the image is inverted.
+                        hasAPPEMarker = true;
+
                         index += calcBytes(this.raw[index + 2],
                                            this.raw[index + 3]) + 2;
                     } else {
@@ -157,13 +176,15 @@ public class JpegImage extends AbstractFopImage {
                          + "JpegImage - Invalid JPEG Header.");
             return false;
         }
-        if (iccStream.size() > 0) {
-            byte[] align = new byte[((iccStream.size()) % 8) + 8];
-            try {
-                iccStream.write(align);
-            } catch (Exception ex) {
-                log.error("Error while aligning ICC stream: " + ex.getMessage(), ex);
-                return false;
+        if (iccStream != null && iccStream.size() > 0) {
+            int padding = (8 - (iccStream.size() % 8)) % 8;
+            if (padding != 0) {
+                try {
+                    iccStream.write(new byte[padding]);
+                } catch (Exception ex) {
+                    log.error("Error while aligning ICC stream: " + ex.getMessage(), ex);
+                    return false;
+                }
             }
             try {
                 iccProfile = ICC_Profile.getInstance(iccStream.toByteArray());
@@ -175,6 +196,9 @@ public class JpegImage extends AbstractFopImage {
             log.error("ColorSpace not specified for JPEG image");
             return false;
         }
+        if (hasAPPEMarker && this.colorSpace.getType() == ColorSpace.TYPE_CMYK) {
+            this.invertImage = true;
+        }
         return true;
     }
 
index 6c8f61e5c582db8ed4ddf3df8004f68357ab96fb..4e6664cff60802aee3e3309bf5ad487af146db1d 100644 (file)
@@ -172,6 +172,11 @@ public class BitmapImage implements PDFImage {
         return maskRef;
     }
 
+    /** @see org.apache.fop.pdf.PDFImage#isInverted() */
+    public boolean isInverted() {
+        return false;
+    }
+    
     /**
      * @see org.apache.fop.pdf.PDFImage#outputContents(OutputStream)
      */
@@ -208,7 +213,7 @@ public class BitmapImage implements PDFImage {
     public PDFFilter getPDFFilter() {
         return null;
     }
-    
+
 }
 
 
index 8be9c28a135a08f28742e84dd53a3ef3aa694a10..23114c65659a1d3acf589f3848785b306517d91c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -109,6 +109,9 @@ public interface PDFImage {
      */
     String getSoftMask();
 
+    /** @return true for CMYK images generated by Adobe Photoshop */
+    boolean isInverted();
+    
     /**
      * Get the PDF Filter to be applied to the image.
      *
index 0607add55a0cf723545f1407d7968549dbda2d25..058628b792cf481c07ef6d78347e2d7c7367182c 100644 (file)
@@ -131,13 +131,18 @@ public class PDFXObject extends AbstractPDFStream {
                   + "\n");
         }
 
-        /* PhotoShop generates CMYK values that's inverse,
-           this will invert the values - too bad if it's not
-           a PhotoShop image...
-         */
-        if (pdfimage.getColorSpace().getColorSpace()
-                == PDFColorSpace.DEVICE_CMYK) {
-            sb.append("/Decode [ 1.0 0.0 1.0 0.0 1.0 0.0 1.1 0.0 ]\n");
+        if (pdfimage.isInverted()) {
+            /* PhotoShop generates CMYK values that's inverse,
+             * this will invert the values - too bad if it's not
+             * a PhotoShop image...
+             */
+            if (pdfimage.getColorSpace().getColorSpace() == PDFColorSpace.DEVICE_CMYK) {
+                sb.append("/Decode [ 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 ]\n");
+            } else if (pdfimage.getColorSpace().getColorSpace() == PDFColorSpace.DEVICE_RGB) {
+                sb.append("/Decode [ 1.0 0.0 1.0 0.0 1.0 0.0 ]\n");
+            } else if (pdfimage.getColorSpace().getColorSpace() == PDFColorSpace.DEVICE_GRAY) {
+                sb.append("/Decode [ 1.0 0.0 ]\n");
+            }
         }
 
         if (pdfimage.isTransparent()) {
index d35204e9e171d1aba2f5c7e5c93c459ac1baf9a1..d1beb09614d4c80826a3dfd560415623f9f0d0cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -81,12 +81,6 @@ public class FopPDFImage implements PDFImage {
             pdfFilter.setApplied(true);
             isDCT = true;
 
-            ICC_Profile prof = fopImage.getICCProfile();
-            PDFColorSpace pdfCS = toPDFColorSpace(fopImage.getColorSpace());
-            if (prof != null) {
-                pdfICCStream = doc.getFactory().makePDFICCStream();
-                pdfICCStream.setColorSpace(prof, pdfCS);
-            }
         } else if ("image/tiff".equals(fopImage.getMimeType())
                     && fopImage instanceof TIFFImage) {
             TIFFImage tiffImage = (TIFFImage) fopImage;
@@ -116,6 +110,12 @@ public class FopPDFImage implements PDFImage {
         } else {
             fopImage.load(FopImage.BITMAP);
         }
+        ICC_Profile prof = fopImage.getICCProfile();
+        PDFColorSpace pdfCS = toPDFColorSpace(fopImage.getColorSpace());
+        if (prof != null) {
+            pdfICCStream = doc.getFactory().makePDFICCStream();
+            pdfICCStream.setColorSpace(prof, pdfCS);
+        }
         //Handle transparency mask if applicable
         if (fopImage.hasSoftMask()) {
             byte [] softMask = fopImage.getSoftMask();
@@ -200,6 +200,11 @@ public class FopPDFImage implements PDFImage {
         return softMaskRef;
     }
 
+    /** @return true for CMYK images generated by Adobe Photoshop */
+    public boolean isInverted() {
+        return fopImage.isInverted();
+    }
+    
     /**
      * @see org.apache.fop.pdf.PDFImage#isPS()
      */
index 5522656e2873e8c5719063663ed2093537333af6..eb7b59e393ab893bc4899d0fc32ac746de9b8d96 100644 (file)
@@ -390,6 +390,10 @@ public class PSGraphics2D extends AbstractGraphics2D {
             return this.mask;
         }
 
+        public boolean isInverted() {
+            return false;
+        }
+        
         public byte[] getBitmaps() {
             return this.bitmaps;
         }