You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ja...@apache.org on 2014/02/20 07:31:05 UTC

svn commit: r1570086 [4/7] - in /pdfbox/trunk: examples/src/main/java/org/apache/pdfbox/examples/pdmodel/ examples/src/main/java/org/apache/pdfbox/examples/util/ pdfbox/src/main/java/org/apache/pdfbox/cos/ pdfbox/src/main/java/org/apache/pdfbox/filter/...

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java?rev=1570086&r1=1570085&r2=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDLab.java Thu Feb 20 06:31:01 2014
@@ -24,270 +24,283 @@ import org.apache.pdfbox.cos.COSName;
 
 import org.apache.pdfbox.pdmodel.common.PDRange;
 
-import java.awt.Transparency;
 import java.awt.color.ColorSpace;
-import java.awt.image.ColorModel;
-import java.awt.image.ComponentColorModel;
-import java.awt.image.DataBuffer;
-
 import java.io.IOException;
 
 /**
- * This class represents a Lab color space.
+ * A Lab colour space is a CIE-based ABC colour space with two transformation stages.
  *
- * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.4 $
+ * @author Ben Litchfield
+ * @author John Hewson
  */
-public class PDLab extends PDColorSpace
+public final class PDLab extends PDCIEBasedColorSpace
 {
-    /**
-     * The name of this color space.
-     */
-    public static final String NAME = "Lab";
+    private static final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
 
     private COSArray array;
     private COSDictionary dictionary;
+    private PDColor initialColor;
 
     /**
-     * Constructor.
+     * Creates a new Lab color space.
      */
     public PDLab()
     {
         array = new COSArray();
         dictionary = new COSDictionary();
-        array.add( COSName.LAB );
-        array.add( dictionary );
+        array.add(COSName.LAB);
+        array.add(dictionary);
     }
 
     /**
-     * Constructor with array.
-     *
-     * @param lab The underlying color space.
+     * Creates a new Lab color space from a PDF array.
+     * @param lab the color space array
      */
-    public PDLab( COSArray lab )
+    public PDLab(COSArray lab)
     {
         array = lab;
-        dictionary = (COSDictionary)array.getObject( 1 );
+        dictionary = (COSDictionary)array.getObject(1);
     }
 
-    /**
-     * This will return the name of the color space.
-     *
-     * @return The name of the color space.
-     */
+    @Override
     public String getName()
     {
-        return NAME;
+        return COSName.LAB.getName();
     }
 
-    /**
-     * Convert this standard java object to a COS object.
-     *
-     * @return The cos object that matches this Java object.
-     */
+    @Override
     public COSBase getCOSObject()
     {
         return array;
     }
 
-    /**
-     * Create a Java colorspace for this colorspace.
-     *
-     * @return A color space that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color space.
-     */
-    protected ColorSpace createColorSpace() throws IOException
+    @Override
+    public float[] toRGB(float[] value)
     {
-        return new ColorSpaceLab(getWhitepoint(), getBlackPoint(), getARange(), getBRange());
+        float minA = getARange().getMin();
+        float maxA = getARange().getMax();
+        float minB = getBRange().getMin();
+        float maxB = getBRange().getMax();
+
+        // scale to range
+        float l = value[0] * 100;
+        float a = minA + (value[1] * (maxA - minA));
+        float b = minB + (value[2] * (maxB - minB));
+
+        return labToRGB(l, a, b, getWhitepoint(), getBlackPoint());
     }
 
-    /**
-     * Create a Java color model for this colorspace.
-     *
-     * @param bpc The number of bits per component.
-     *
-     * @return A color model that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color model.
-     */
-    public ColorModel createColorModel( int bpc ) throws IOException
+    // CIE LAB to RGB, see http://en.wikipedia.org/wiki/Lab_color_space
+    private float[] labToRGB(float l, float a, float b,
+                             PDTristimulus whitepoint,
+                             PDTristimulus blackpoint)
     {
-        int[] nBits = {bpc, bpc, bpc};
-        return new ComponentColorModel( getJavaColorSpace(),
-                   nBits,
-                   false,
-                   false,
-                   Transparency.OPAQUE,
-                   DataBuffer.TYPE_BYTE);
+        // L*
+        float lstar = (l + 16f) * (1f / 116f);
+
+        // white point
+        float wpX = whitepoint.getX();
+        float wpY = whitepoint.getY();
+        float wpZ = whitepoint.getZ();
+
+        // TODO: how to use the blackpoint? scale linearly between black & white?
+
+        // XYZ
+        float x = wpX * inverse(lstar + a * (1f / 500f));
+        float y = wpY * inverse(lstar);
+        float z = wpZ * inverse(lstar - b * (1f / 200f));
+
+        // XYZ to RGB
+        return CIEXYZ.toRGB(new float[] { x, y, z });
     }
 
-    /**
-     * This will get the number of components that this color space is made up of.
-     *
-     * @return The number of components in this color space.
-     *
-     * @throws IOException If there is an error getting the number of color components.
-     */
-    public int getNumberOfComponents() throws IOException
+    // reverse transformation (f^-1)
+    private float inverse(float x)
+    {
+        if (x > 6.0 / 29.0)
+        {
+            return x * x * x;
+        }
+        else
+        {
+            return (108f / 841f) * (x - (4f / 29f));
+        }
+    }
+
+    @Override
+    public int getNumberOfComponents()
     {
         return 3;
     }
 
-    /**
-     * This will return the whitepoint tristimulus.  As this is a required field
-     * this will never return null.  A default of 1,1,1 will be returned if the
-     * pdf does not have any values yet.
-     *
-     * @return The whitepoint tristimulus.
-     */
-    public PDTristimulus getWhitepoint()
+    @Override
+    public float[] getDefaultDecode()
     {
-        COSArray wp = (COSArray)dictionary.getDictionaryObject( COSName.WHITE_POINT );
-        if( wp == null )
+        PDRange a = getARange();
+        PDRange b = getARange();
+        return new float[] { 0, 100, a.getMin(), a.getMax(), b.getMin(), b.getMax() };
+    }
+
+    @Override
+    public PDColor getInitialColor()
+    {
+        if (initialColor != null)
         {
-            wp = new COSArray();
-            wp.add( new COSFloat( 1.0f ) );
-            wp.add( new COSFloat( 1.0f ) );
-            wp.add( new COSFloat( 1.0f ) );
-            dictionary.setItem( COSName.WHITE_POINT, wp );
+            initialColor = new PDColor(new float[] {
+                    0,
+                    Math.max(0, getARange().getMin()),
+                    Math.max(0, getBRange().getMin()) });
         }
-        return new PDTristimulus( wp );
+        return initialColor;
     }
 
     /**
-     * This will set the whitepoint tristimulus.  As this is a required field
-     * this null should not be passed into this function.
-     *
-     * @param wp The whitepoint tristimulus.
+     * This will return the whitepoint tristimulus.
+     * As this is a required field this will never return null.
+     * A default of 1,1,1 will be returned if the pdf does not have any values yet.
+     * @return the whitepoint tristimulus
      */
-    public void setWhitepoint( PDTristimulus wp )
+    public PDTristimulus getWhitepoint()
     {
-        COSBase wpArray = wp.getCOSObject();
-        if( wpArray != null )
+        COSArray wp = (COSArray)dictionary.getDictionaryObject(COSName.WHITE_POINT);
+        if(wp == null)
         {
-            dictionary.setItem( COSName.WHITE_POINT, wpArray );
+            wp = new COSArray();
+            wp.add(new COSFloat(1.0f));
+            wp.add(new COSFloat(1.0f));
+            wp.add(new COSFloat(1.0f));
+            dictionary.setItem(COSName.WHITE_POINT, wp);
         }
+        return new PDTristimulus(wp);
     }
 
     /**
-     * This will return the BlackPoint tristimulus.  This is an optional field but
-     * has defaults so this will never return null.
+     * This will return the BlackPoint tristimulus.
+     * This is an optional field but has defaults so this will never return null.
      * A default of 0,0,0 will be returned if the pdf does not have any values yet.
-     *
-     * @return The blackpoint tristimulus.
+     * @return the blackpoint tristimulus
      */
     public PDTristimulus getBlackPoint()
     {
-        COSArray bp = (COSArray)dictionary.getDictionaryObject( COSName.BLACK_POINT );
-        if( bp == null )
+        COSArray bp = (COSArray)dictionary.getDictionaryObject(COSName.BLACK_POINT);
+        if(bp == null)
         {
             bp = new COSArray();
-            bp.add( new COSFloat( 0.0f ) );
-            bp.add( new COSFloat( 0.0f ) );
-            bp.add( new COSFloat( 0.0f ) );
-            dictionary.setItem( COSName.BLACK_POINT, bp );
+            bp.add(new COSFloat(0.0f));
+            bp.add(new COSFloat(0.0f));
+            bp.add(new COSFloat(0.0f));
+            dictionary.setItem(COSName.BLACK_POINT, bp);
         }
-        return new PDTristimulus( bp );
+        return new PDTristimulus(bp);
+    }
+
+    private COSArray getRangeArray()
+    {
+        COSArray range = (COSArray)dictionary.getDictionaryObject(COSName.RANGE);
+        if(range == null)
+        {
+            range = new COSArray();
+            dictionary.setItem(COSName.RANGE, array);
+            range.add(new COSFloat(-100));
+            range.add(new COSFloat(100));
+            range.add(new COSFloat(-100));
+            range.add(new COSFloat(100));
+        }
+        return range;
     }
 
     /**
-     * This will set the BlackPoint tristimulus.  As this is a required field
-     * this null should not be passed into this function.
-     *
-     * @param bp The BlackPoint tristimulus.
+     * This will get the valid range for the "a" component.
+     * If none is found then the default will be returned, which is -100 to 100.
+     * @return the "a" range
      */
-    public void setBlackPoint( PDTristimulus bp )
+    public PDRange getARange()
     {
+        COSArray range = getRangeArray();
+        return new PDRange(range, 0);
+    }
 
-        COSBase bpArray = null;
-        if( bp != null )
-        {
-            bpArray = bp.getCOSObject();
-        }
-        dictionary.setItem( COSName.BLACK_POINT, bpArray );
+    /**
+     * This will get the valid range for the "b" component.
+     * If none is found  then the default will be returned, which is -100 to 100.
+     * @return the "b" range
+     */
+    public PDRange getBRange()
+    {
+        COSArray range = getRangeArray();
+        return new PDRange(range, 1);
     }
 
-    private COSArray getRangeArray()
+    /**
+     * This will set the whitepoint tristimulus.
+     * As this is a required field this null should not be passed into this function.
+     * @param whitepoint the whitepoint tristimulus
+     */
+    public void setWhitepoint(PDTristimulus whitepoint)
     {
-        COSArray range = (COSArray)dictionary.getDictionaryObject( COSName.RANGE );
-        if( range == null )
+        COSBase wpArray = whitepoint.getCOSObject();
+        if(wpArray != null)
         {
-            range = new COSArray();
-            dictionary.setItem( COSName.RANGE, array );
-            range.add( new COSFloat( -100 ) );
-            range.add( new COSFloat( 100 ) );
-            range.add( new COSFloat( -100 ) );
-            range.add( new COSFloat( 100 ) );
+            dictionary.setItem(COSName.WHITE_POINT, wpArray);
         }
-        return range;
     }
 
     /**
-     * This will get the valid range for the a component.  If none is found
-     * then the default will be returned, which is -100 to 100.
-     *
-     * @return The a range.
+     * This will set the BlackPoint tristimulus.
+     * As this is a required field this null should not be passed into this function.
+     * @param blackpoint the BlackPoint tristimulus
      */
-    public PDRange getARange()
+    public void setBlackPoint(PDTristimulus blackpoint)
     {
-        COSArray range = getRangeArray();
-        return new PDRange( range, 0 );
+        COSBase bpArray = null;
+        if(blackpoint != null)
+        {
+            bpArray = blackpoint.getCOSObject();
+        }
+        dictionary.setItem(COSName.BLACK_POINT, bpArray);
     }
 
     /**
-     * This will set the a range for this color space.
-     *
-     * @param range The new range for the a component.
+     * This will set the a range for the "a" component.
+     * @param range the new range for the "a" component
      */
-    public void setARange( PDRange range )
+    public void setARange(PDRange range)
     {
         COSArray rangeArray = null;
         //if null then reset to defaults
-        if( range == null )
+        if(range == null)
         {
             rangeArray = getRangeArray();
-            rangeArray.set( 0, new COSFloat( -100 ) );
-            rangeArray.set( 1, new COSFloat( 100 ) );
+            rangeArray.set(0, new COSFloat(-100));
+            rangeArray.set(1, new COSFloat(100));
         }
         else
         {
             rangeArray = range.getCOSArray();
         }
-        dictionary.setItem( COSName.RANGE, rangeArray );
-    }
-
-    /**
-     * This will get the valid range for the b component.  If none is found
-     * then the default will be returned, which is -100 to 100.
-     *
-     * @return The b range.
-     */
-    public PDRange getBRange()
-    {
-        COSArray range = getRangeArray();
-        return new PDRange( range, 1 );
+        dictionary.setItem(COSName.RANGE, rangeArray);
+        initialColor = null;
     }
 
     /**
-     * This will set the b range for this color space.
-     *
-     * @param range The new range for the b component.
+     * This will set the "b" range for this color space.
+     * @param range the new range for the "b" component
      */
-    public void setBRange( PDRange range )
+    public void setBRange(PDRange range)
     {
         COSArray rangeArray = null;
         //if null then reset to defaults
-        if( range == null )
+        if(range == null)
         {
             rangeArray = getRangeArray();
-            rangeArray.set( 2, new COSFloat( -100 ) );
-            rangeArray.set( 3, new COSFloat( 100 ) );
+            rangeArray.set(2, new COSFloat(-100));
+            rangeArray.set(3, new COSFloat(100));
         }
         else
         {
             rangeArray = range.getCOSArray();
         }
-        dictionary.setItem( COSName.RANGE, rangeArray );
+        dictionary.setItem(COSName.RANGE, rangeArray);
+        initialColor = null;
     }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDOutputIntent.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDOutputIntent.java?rev=1570086&r1=1570085&r2=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDOutputIntent.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDOutputIntent.java Thu Feb 20 06:31:01 2014
@@ -27,72 +27,90 @@ import org.apache.pdfbox.pdmodel.PDDocum
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.pdmodel.common.PDStream;
 
-public class PDOutputIntent implements COSObjectable { 
-    
-    private COSDictionary dictionary; 
-    
-    public PDOutputIntent(PDDocument doc, InputStream colorProfile) throws Exception{ 
-        dictionary = new COSDictionary(); 
-        dictionary.setItem(COSName.TYPE, COSName.OUTPUT_INTENT); 
-        dictionary.setItem(COSName.S, COSName.GTS_PDFA1); 
+/**
+ * An Output Intent describes the colour reproduction characteristics of a possible output
+ * device or production condition.
+ * Output intents provide a means for matching the colour characteristics of a PDF document with
+ * those of a target output device or production environment in which the document will be printed.
+ *
+ * @author Guillaume Bailleul
+ */
+public final class PDOutputIntent implements COSObjectable
+{
+    private COSDictionary dictionary;
+
+    public PDOutputIntent(PDDocument doc, InputStream colorProfile) throws Exception
+    {
+        dictionary = new COSDictionary();
+        dictionary.setItem(COSName.TYPE, COSName.OUTPUT_INTENT);
+        dictionary.setItem(COSName.S, COSName.GTS_PDFA1);
         PDStream destOutputIntent = configureOutputProfile(doc, colorProfile);
-        dictionary.setItem(COSName.DEST_OUTPUT_PROFILE, destOutputIntent); 
-    } 
+        dictionary.setItem(COSName.DEST_OUTPUT_PROFILE, destOutputIntent);
+    }
 
-    public PDOutputIntent (COSDictionary dictionary) {
+    public PDOutputIntent(COSDictionary dictionary)
+    {
         this.dictionary = dictionary;
     }
-    
-    public COSBase getCOSObject() { 
-        return dictionary; 
-    } 
 
-    public COSStream getDestOutputIntent () {
-        return (COSStream)dictionary.getItem(COSName.DEST_OUTPUT_PROFILE);
+    public COSBase getCOSObject()
+    {
+        return dictionary;
+    }
+
+    public COSStream getDestOutputIntent()
+    {
+        return (COSStream) dictionary.getItem(COSName.DEST_OUTPUT_PROFILE);
     }
-    
-    public String getInfo () {
+
+    public String getInfo()
+    {
         return dictionary.getString(COSName.INFO);
     }
-    
-    public void setInfo( String value ) 
-    { 
-        dictionary.setString(COSName.INFO, value); 
-    } 
 
-    public String getOutputCondition () {
+    public void setInfo(String value)
+    {
+        dictionary.setString(COSName.INFO, value);
+    }
+
+    public String getOutputCondition()
+    {
         return dictionary.getString(COSName.OUTPUT_CONDITION);
     }
-    
-    public void setOutputCondition( String value ) 
-    { 
-        dictionary.setString(COSName.OUTPUT_CONDITION, value); 
-    } 
 
-    public String getOutputConditionIdentifier () {
+    public void setOutputCondition(String value)
+    {
+        dictionary.setString(COSName.OUTPUT_CONDITION, value);
+    }
+
+    public String getOutputConditionIdentifier()
+    {
         return dictionary.getString(COSName.OUTPUT_CONDITION_IDENTIFIER);
     }
-    
-    public void setOutputConditionIdentifier( String value ) 
-    { 
-        dictionary.setString(COSName.OUTPUT_CONDITION_IDENTIFIER, value); 
-    } 
 
-    public String getRegistryName () {
+    public void setOutputConditionIdentifier(String value)
+    {
+        dictionary.setString(COSName.OUTPUT_CONDITION_IDENTIFIER, value);
+    }
+
+    public String getRegistryName()
+    {
         return dictionary.getString(COSName.REGISTRY_NAME);
     }
-    
-    public void setRegistryName( String value ) 
-    { 
-        dictionary.setString(COSName.REGISTRY_NAME, value); 
-    } 
-    
-    private PDStream configureOutputProfile (PDDocument doc, InputStream colorProfile) throws IOException {
-        PDStream stream = new PDStream(doc,colorProfile, false); 
-        stream.getStream().setFilters(COSName.FLATE_DECODE); 
-        stream.getStream().setInt( COSName.LENGTH, stream.getByteArray().length ); 
-        stream.getStream().setInt(COSName.N, 3); 
-        stream.addCompression(); 
+
+    public void setRegistryName(String value)
+    {
+        dictionary.setString(COSName.REGISTRY_NAME, value);
+    }
+
+    private PDStream configureOutputProfile(PDDocument doc, InputStream colorProfile)
+            throws IOException
+    {
+        PDStream stream = new PDStream(doc, colorProfile, false);
+        stream.getStream().setFilters(COSName.FLATE_DECODE);
+        stream.getStream().setInt(COSName.LENGTH, stream.getByteArray().length);
+        stream.getStream().setInt(COSName.N, 3);
+        stream.addCompression();
         return stream;
     }
 }  

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDPattern.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDPattern.java?rev=1570086&r1=1570085&r2=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDPattern.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDPattern.java Thu Feb 20 06:31:01 2014
@@ -16,93 +16,166 @@
  */
 package org.apache.pdfbox.pdmodel.graphics.color;
 
-import org.apache.pdfbox.cos.COSArray;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.graphics.pattern.PDPatternResources;
+import org.apache.pdfbox.pdmodel.graphics.pattern.PDShadingPatternResources;
+import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPatternResources;
+import org.apache.pdfbox.pdmodel.graphics.pattern.tiling.ColoredTilingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.AxialShadingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingResources;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType1;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType2;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType3;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType4;
+import org.apache.pdfbox.pdmodel.graphics.shading.PDShadingType5;
+import org.apache.pdfbox.pdmodel.graphics.shading.RadialShadingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.Type1ShadingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.Type4ShadingPaint;
+import org.apache.pdfbox.pdmodel.graphics.shading.Type5ShadingPaint;
+
+import java.awt.Color;
+import java.awt.Paint;
+import java.awt.image.BufferedImage;
 
-import java.awt.color.ColorSpace;
-import java.awt.image.ColorModel;
-
+import java.awt.image.WritableRaster;
 import java.io.IOException;
+import java.util.Map;
 
 /**
- * This class represents a Pattern color space.
+ * A Pattern color space is either a Tiling pattern or a Shading pattern.
  *
- * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.4 $
+ * @author John Hewson
+ * @author Ben Litchfield
  */
-public class PDPattern extends PDColorSpace
+public final class PDPattern extends PDSpecialColorSpace
 {
-    private COSArray array;
+    private static final Log LOG = LogFactory.getLog(PDPattern.class);
 
-    /**
-     * The name of this color space.
-     */
-    public static final String NAME = "Pattern";
+    private Map<String, PDPatternResources> patterns;
 
     /**
-     * Default constructor.
+     * Creates a new Pattern color space.
      */
-    public PDPattern()
+    public PDPattern(Map<String, PDPatternResources> patterns)
     {
-        array = new COSArray();
-        array.add( COSName.PATTERN );
+        this.patterns = patterns;
     }
 
-    /**
-     * Constructor.
-     *
-     * @param pattern The pattern array.
-     */
-    public PDPattern( COSArray pattern)
+    @Override
+    public String getName()
     {
-        array = pattern;
+        return COSName.PATTERN.getName();
     }
 
-    /**
-     * This will return the name of the color space.
-     *
-     * @return The name of the color space.
-     */
-    public String getName()
+    @Override
+    public int getNumberOfComponents()
     {
-        return NAME;
+        throw new UnsupportedOperationException();
     }
 
-    /**
-     * This will get the number of components that this color space is made up of.
-     *
-     * @return The number of components in this color space.
-     *
-     * @throws IOException If there is an error getting the number of color components.
-     */
-    public int getNumberOfComponents() throws IOException
+    @Override
+    public float[] getDefaultDecode()
     {
-        return -1;
+        throw new UnsupportedOperationException();
     }
 
-    /**
-     * Create a Java colorspace for this colorspace.
-     *
-     * @return A color space that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color space.
-     */
-    protected ColorSpace createColorSpace() throws IOException
+    @Override
+    public PDColor getInitialColor()
     {
-        throw new IOException( "Not implemented" );
+        return PDColor.EMPTY_PATTERN;
     }
 
-    /**
-     * Create a Java color model for this colorspace.
-     *
-     * @param bpc The number of bits per component.
-     *
-     * @return A color model that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color model.
-     */
-    public ColorModel createColorModel( int bpc ) throws IOException
+    @Override
+    public float[] toRGB(float[] value)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BufferedImage toRGBImage(WritableRaster raster) throws IOException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Paint toPaint(PDColor color) throws IOException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Paint toPaint(PDColor color, int pageHeight) throws IOException
+    {
+        if (!patterns.containsKey(color.getPatternName()))
+        {
+            throw new IOException("pattern " + color.getPatternName() + " was not found");
+        }
+
+        PDPatternResources pattern = patterns.get(color.getPatternName());
+        if (pattern instanceof PDTilingPatternResources)
+        {
+            return toTilingPaint((PDTilingPatternResources)pattern, color);
+        }
+        else
+        {
+            return toShadingPaint((PDShadingPatternResources)pattern, pageHeight);
+        }
+    }
+
+    public Paint toTilingPaint(PDTilingPatternResources tilingPattern, PDColor color)
+            throws IOException
+    {
+        if (tilingPattern.getPatternType() == PDTilingPatternResources.COLORED_TILING_PATTERN)
+        {
+            // colored tiling pattern
+            // TODO we should be passing the color to ColoredTilingPaint
+            return new ColoredTilingPaint(tilingPattern);
+        }
+        else
+        {
+            // uncolored tiling pattern
+            // TODO ...
+            LOG.debug("Not implemented: uncoloured tiling patterns");
+            return new Color(0, 0, 0, 0); // transparent
+        }
+    }
+    public Paint toShadingPaint(PDShadingPatternResources shadingPattern, int pageHeight)
+            throws IOException
+    {
+        PDShadingResources shadingResources = shadingPattern.getShading();
+        int shadingType = shadingResources != null ? shadingResources.getShadingType() : 0;
+        switch (shadingType)
+        {
+            case PDShadingResources.SHADING_TYPE1:
+                return new Type1ShadingPaint((PDShadingType1)shadingResources,
+                                             shadingPattern.getMatrix(), pageHeight);
+            case PDShadingResources.SHADING_TYPE2:
+                return new AxialShadingPaint((PDShadingType2)shadingResources,
+                                             shadingPattern.getMatrix(), pageHeight);
+            case PDShadingResources.SHADING_TYPE3:
+                return new RadialShadingPaint((PDShadingType3)shadingResources,
+                                              shadingPattern.getMatrix(), pageHeight);
+            case PDShadingResources.SHADING_TYPE4:
+                return new Type4ShadingPaint((PDShadingType4)shadingResources,
+                                             shadingPattern.getMatrix(), pageHeight);
+            case PDShadingResources.SHADING_TYPE5:
+                return new Type5ShadingPaint((PDShadingType5)shadingResources,
+                                             shadingPattern.getMatrix(), pageHeight);
+            case PDShadingResources.SHADING_TYPE6:
+            case PDShadingResources.SHADING_TYPE7:
+                // TODO ...
+                LOG.debug("Not implemented, shading type: " + shadingType);
+                return new Color(0, 0, 0, 0); // transparent
+            default:
+                throw new IOException("Invalid shading type: " + shadingType);
+        }
+    }
+
+    @Override
+    public String toString()
     {
-        throw new IOException( "Not implemented" );
+        return "Pattern";
     }
 }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java?rev=1570086&r1=1570085&r2=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSeparation.java Thu Feb 20 06:31:01 2014
@@ -16,205 +16,185 @@
  */
 package org.apache.pdfbox.pdmodel.graphics.color;
 
-import java.awt.color.ColorSpace;
-import java.awt.image.ColorModel;
+import java.awt.Point;
+import java.awt.image.BufferedImage;
 
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
 import java.io.IOException;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.common.function.PDFunction;
 
 /**
- * This class represents a Separation color space.
+ * A Separation color space used to specify either additional colorants or for isolating the
+ * control of individual colour components of a device colour space for a subtractive device.
+ * When such a space is the current colour space, the current colour shall be a single-component
+ * value, called a tint, that controls the given colorant or colour components only.
  *
- * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.5 $
+ * @author Ben Litchfield
+ * @author John Hewson
  */
-public class PDSeparation extends PDColorSpace
+public class PDSeparation extends PDSpecialColorSpace
 {
-    /**
-     * Log instance.
-     */
-    private static final Log log = LogFactory.getLog(PDSeparation.class);
+    private static final PDColor INITIAL_COLOR = new PDColor(new float[] { 1 });
 
-    /**
-     * The name of this color space.
-     */
-    public static final String NAME = "Separation";
+    // array indexes
+    private static final int COLORANT_NAMES = 1;
+    private static final int ALTERNATE_CS = 2;
+    private static final int TINT_TRANSFORM = 3;
 
+    // fields
+    private PDColorSpace alternateColorSpace = null;
+    private PDFunction tintTransform = null;
 
     /**
-     * Constructor.
+     * Creates a new Separation color space.
      */
     public PDSeparation()
     {
         array = new COSArray();
-        array.add( COSName.SEPARATION );
-        array.add( COSName.getPDFName( "" ) );
+        array.add(COSName.SEPARATION);
+        array.add(COSName.getPDFName(""));
     }
 
     /**
-     * Constructor.
-     *
-     * @param separation The array containing all separation information.
+     * Creates a new Separation color space from a PDF color space array.
+     * @param separation an array containing all separation information
      */
-    public PDSeparation( COSArray separation )
+    public PDSeparation(COSArray separation) throws IOException
     {
         array = separation;
+        alternateColorSpace = PDColorSpace.create(array.getObject(ALTERNATE_CS));
+        tintTransform = PDFunction.create(array.getObject(TINT_TRANSFORM));
     }
 
-    /**
-     * This will return the name of the color space.  For a PDSeparation object
-     * this will always return "Separation"
-     *
-     * @return The name of the color space.
-     */
+    @Override
     public String getName()
     {
-        return NAME;
+        return COSName.SEPARATION.getName();
     }
 
-    /**
-     * This will get the number of components that this color space is made up of.
-     *
-     * @return The number of components in this color space.
-     *
-     * @throws IOException If there is an error getting the number of color components.
-     */
-    public int getNumberOfComponents() throws IOException
+    @Override
+    public int getNumberOfComponents()
     {
-        return getAlternateColorSpace().getNumberOfComponents();
+        return 1;
     }
 
-    /**
-     * Create a Java colorspace for this colorspace.
-     *
-     * @return A color space that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color space.
-     */
-    protected ColorSpace createColorSpace() throws IOException
+    @Override
+    public float[] getDefaultDecode()
     {
-        try
-        {
-            PDColorSpace alt = getAlternateColorSpace();
-            return alt.getJavaColorSpace();
-        }
-        catch (IOException ioexception)
-        {
-            log.error(ioexception, ioexception);
+        return new float[] { 0, 1 };
+    }
 
-            throw ioexception;
-        }
-        catch (Exception exception)
-        {
-            log.error(exception, exception);
-            throw new IOException("Failed to Create ColorSpace");
-        }
+    @Override
+    public PDColor getInitialColor()
+    {
+        return INITIAL_COLOR;
     }
 
-    /**
-     * Create a Java color model for this colorspace.
-     *
-     * @param bpc The number of bits per component.
-     *
-     * @return A color model that can be used for Java AWT operations.
-     *
-     * @throws IOException If there is an error creating the color model.
-     */
-    public ColorModel createColorModel( int bpc ) throws IOException
+    @Override
+    public float[] toRGB(float[] value) throws IOException
     {
-        log.info("About to create ColorModel for " + getAlternateColorSpace().toString());
-        return getAlternateColorSpace().createColorModel(bpc);
+        float[] altColor = tintTransform.eval(value);
+        return alternateColorSpace.toRGB(altColor);
     }
 
-    /**
-     * This will get the separation name.
-     *
-     * @return The name in the separation.
-     */
-    public String getColorantName()
+    //
+    // WARNING: this method is performance sensitive, modify with care!
+    //
+    @Override
+    public BufferedImage toRGBImage(WritableRaster raster) throws IOException
     {
-        COSName name = (COSName)array.getObject( 1 );
-        return name.getName();
+        // use the tint transform to convert the sample into
+        // the alternate color space (this is usually 1:many)
+        WritableRaster altRaster = Raster.createBandedRaster(DataBuffer.TYPE_BYTE,
+                raster.getWidth(), raster.getHeight(),
+                alternateColorSpace.getNumberOfComponents(),
+                new Point(0, 0));
+
+        int numAltComponents = alternateColorSpace.getNumberOfComponents();
+        int width = raster.getWidth();
+        int height = raster.getHeight();
+        float[] samples = new float[1];
+        int[] alt = new int[numAltComponents];
+        for (int y = 0; y < height; y++)
+        {
+            for (int x = 0; x < width; x++)
+            {
+                raster.getPixel(x, y, samples);
+                samples[0] /= 255; // 0..1
+
+                // TODO special colorants: None, All
+
+                float[] result = tintTransform.eval(samples);
+                for (int s = 0; s < numAltComponents; s++)
+                {
+                    // scale to 0..255
+                    alt[s] = (int)(result[s] * 255);
+                }
+
+                altRaster.setPixel(x, y, alt);
+            }
+        }
+
+        // convert the alternate color space to RGB
+        return alternateColorSpace.toRGBImage(altRaster);
     }
 
     /**
-     * This will set the separation name.
-     *
-     * @param name The separation name.
+     * Returns the colorant name.
+     * @return the name of the colorant
      */
-    public void setColorantName( String name )
+    public String getColorantName()
     {
-        array.set( 1, COSName.getPDFName( name ) );
+        COSName name = (COSName)array.getObject(COLORANT_NAMES);
+        return name.getName();
     }
 
     /**
-     * This will get the alternate color space for this separation.
-     *
-     * @return The alternate color space.
-     *
-     * @throws IOException If there is an error getting the alternate color space.
+     * Sets the colorant name.
+     * @param name the name of the colorant
      */
-    public PDColorSpace getAlternateColorSpace() throws IOException
+    public void setColorantName(String name)
     {
-        COSBase alternate = array.getObject( 2 );
-        PDColorSpace cs = PDColorSpaceFactory.createColorSpace( alternate );
-        return cs;
+        array.set(1, COSName.getPDFName(name));
     }
 
     /**
-     * This will set the alternate color space.
-     *
-     * @param cs The alternate color space.
+     * Sets the alternate color space.
+     * @param colorSpace The alternate color space.
      */
-    public void setAlternateColorSpace( PDColorSpace cs )
+    public void setAlternateColorSpace(PDColorSpace colorSpace)
     {
+        alternateColorSpace = colorSpace;
         COSBase space = null;
-        if( cs != null )
+        if (colorSpace != null)
         {
-            space = cs.getCOSObject();
+            space = colorSpace.getCOSObject();
         }
-        array.set( 2, space );
+        array.set(ALTERNATE_CS, space);
     }
 
     /**
-     * This will get the tint transform function.
-     *
-     * @return The tint transform function.
-     *
-     * @throws IOException If there is an error creating the PDFunction
+     * Sets the tint transform function.
+     * @param tint the tint transform function
      */
-    public PDFunction getTintTransform() throws IOException
+    public void setTintTransform(PDFunction tint)
     {
-        return PDFunction.create( array.getObject( 3 ) );
+        tintTransform = tint;
+        array.set(TINT_TRANSFORM, tint);
     }
 
-    /**
-     * This will set the tint transform function.
-     *
-     * @param tint The tint transform function.
-     */
-    public void setTintTransform( PDFunction tint )
-    {
-        array.set( 3, tint );
-    }
-
-    /**
-     * Returns the components of the color in the alternate colorspace for the given tint value.
-     * @param tintValue the tint value
-     * @return COSArray with the color components
-     * @throws IOException If the tint function is not supported
-     */
-    public COSArray calculateColorValues(COSBase tintValue) throws IOException
+    @Override
+    public String toString()
     {
-        PDFunction tintTransform = getTintTransform();
-        COSArray tint = new COSArray();
-        tint.add(tintValue);
-        return tintTransform.eval(tint);
+        return getName() + "{" +
+                "\"" + getColorantName() + "\"" + " " +
+                alternateColorSpace.getName() + " " +
+                tintTransform + "}";
     }
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSpecialColorSpace.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSpecialColorSpace.java?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSpecialColorSpace.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDSpecialColorSpace.java Thu Feb 20 06:31:01 2014
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.graphics.color;
+
+import org.apache.pdfbox.cos.COSBase;
+
+/**
+ * Special colour spaces add features or properties to an underlying colour space.
+ *
+ * @author John Hewson
+ */
+public abstract class PDSpecialColorSpace extends PDColorSpace
+{
+    @Override
+    public COSBase getCOSObject()
+    {
+        return array;
+    }
+}

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDTristimulus.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDTristimulus.java?rev=1570086&r1=1570085&r2=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDTristimulus.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/color/PDTristimulus.java Thu Feb 20 06:31:01 2014
@@ -24,55 +24,50 @@ import org.apache.pdfbox.cos.COSNumber;
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 
 /**
- * A tristimulus, or collection of three floating point parameters used for
- * color operations.
+ * A tristimulus, or collection of three floating point parameters used for color operations.
  *
- * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
- * @version $Revision: 1.2 $
+ * @author Ben Litchfield
  */
-public class PDTristimulus implements COSObjectable
+public final class PDTristimulus implements COSObjectable
 {
     private COSArray values = null;
 
     /**
-     * Constructor.  Defaults all values to 0, 0, 0.
+     * Constructor. Defaults all values to 0, 0, 0.
      */
     public PDTristimulus()
     {
         values = new COSArray();
-        values.add( new COSFloat( 0.0f ) );
-        values.add( new COSFloat( 0.0f ) );
-        values.add( new COSFloat( 0.0f ) );
+        values.add(new COSFloat(0.0f));
+        values.add(new COSFloat(0.0f));
+        values.add(new COSFloat(0.0f));
     }
 
     /**
      * Constructor from COS object.
-     *
-     * @param array The array containing the XYZ values.
+     * @param array the array containing the XYZ values
      */
-    public PDTristimulus( COSArray array )
+    public PDTristimulus(COSArray array)
     {
         values = array;
     }
 
     /**
      * Constructor from COS object.
-     *
-     * @param array The array containing the XYZ values.
+     * @param array the array containing the XYZ values
      */
-    public PDTristimulus( float[] array )
+    public PDTristimulus(float[] array)
     {
         values = new COSArray();
-        for( int i=0; i<array.length && i<3; i++ )
+        for(int i=0; i<array.length && i<3; i++)
         {
-            values.add( new COSFloat( array[i] ) );
+            values.add(new COSFloat(array[i]));
         }
     }
 
     /**
      * Convert this standard java object to a COS object.
-     *
-     * @return The cos object that matches this Java object.
+     * @return the cos object that matches this Java object
      */
     public COSBase getCOSObject()
     {
@@ -80,62 +75,56 @@ public class PDTristimulus implements CO
     }
 
     /**
-     * This will get the x value of the tristimulus.
-     *
-     * @return The X value.
+     * Returns the x value of the tristimulus.
+     * @return the X value
      */
     public float getX()
     {
-        return ((COSNumber)values.get( 0 )).floatValue();
+        return ((COSNumber)values.get(0)).floatValue();
     }
 
     /**
-     * This will set the x value of the tristimulus.
-     *
-     * @param x The x value for the tristimulus.
+     * Sets the x value of the tristimulus.
+     * @param x the x value for the tristimulus
      */
-    public void setX( float x )
+    public void setX(float x)
     {
-        values.set( 0, new COSFloat( x ) );
+        values.set(0, new COSFloat(x));
     }
 
     /**
-     * This will get the y value of the tristimulus.
-     *
-     * @return The Y value.
+     * Returns the y value of the tristimulus.
+     * @return the Y value
      */
     public float getY()
     {
-        return ((COSNumber)values.get( 1 )).floatValue();
+        return ((COSNumber)values.get(1)).floatValue();
     }
 
     /**
-     * This will set the y value of the tristimulus.
-     *
-     * @param y The y value for the tristimulus.
+     * Sets the y value of the tristimulus.
+     * @param y the y value for the tristimulus
      */
-    public void setY( float y )
+    public void setY(float y)
     {
-        values.set( 1, new COSFloat( y ) );
+        values.set(1, new COSFloat(y));
     }
 
     /**
-     * This will get the z value of the tristimulus.
-     *
-     * @return The Z value.
+     * Returns the z value of the tristimulus.
+     * @return the Z value
      */
     public float getZ()
     {
-        return ((COSNumber)values.get( 2 )).floatValue();
+        return ((COSNumber)values.get(2)).floatValue();
     }
 
     /**
-     * This will set the z value of the tristimulus.
-     *
-     * @param z The z value for the tristimulus.
+     * Sets the z value of the tristimulus.
+     * @param z the z value for the tristimulus
      */
-    public void setZ( float z )
+    public void setZ(float z)
     {
-        values.set( 2, new COSFloat( z ) );
+        values.set(2, new COSFloat(z));
     }
 }

Copied: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/PDFormXObject.java (from r1568222, pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java)
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/PDFormXObject.java?p2=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/PDFormXObject.java&p1=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java&r1=1568222&r2=1570086&rev=1570086&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/xobject/PDXObjectForm.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/PDFormXObject.java Thu Feb 20 06:31:01 2014
@@ -14,60 +14,84 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.pdfbox.pdmodel.graphics.xobject;
+package org.apache.pdfbox.pdmodel.graphics.form;
 
 import java.awt.geom.AffineTransform;
+import java.util.Map;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSFloat;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSNumber;
 import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDResources;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.PDXObject;
 import org.apache.pdfbox.util.Matrix;
 
+/*
+TODO There are further Form XObjects to implement:
+
++ PDFormXObject
+|- PDReferenceXObject
+|- PDGroupXObject
+   |- PDTransparencyXObject
+
+See PDF 32000 p111
+
+When doing this all methods on PDFormXObject should probably be made
+final and all fields private.
+*/
+
 /**
- * A form xobject.
- * 
- * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
+ * A Form XObject.
  * 
+ * @author Ben Litchfield
  */
-public class PDXObjectForm extends PDXObject
+public final class PDFormXObject extends PDXObject
 {
+    private static final Log LOG = LogFactory.getLog(PDFormXObject.class);
+
+    // name of XObject in resources, to prevent recursion
+    private String name;
 
     /**
-     * The XObject subtype.
+     * Creates a Form XObject for reading.
+     * @param stream The XObject stream
      */
-    public static final String SUB_TYPE = "Form";
-    
+    public PDFormXObject(PDStream stream)
+    {
+        super(stream, COSName.FORM);
+    }
+
     /**
-     * Standard constructor.
-     * 
-     * @param formStream The XObject is passed as a COSStream.
+     * Creates a Form XObject for reading.
+     * @param stream The XObject stream
+     * @param name The name of the form XObject, to prevent recursion.
      */
-    public PDXObjectForm(PDStream formStream)
+    public PDFormXObject(PDStream stream, String name)
     {
-        super(formStream);
-        getCOSStream().setName(COSName.SUBTYPE, SUB_TYPE);
+        super(stream, COSName.FORM);
+        this.name = name;
     }
 
     /**
-     * Standard constructor.
-     * 
-     * @param formStream The XObject is passed as a COSStream.
+     * Creates a Form Image XObject for writing, in the given document.
+     * @param document The current document
      */
-    public PDXObjectForm(COSStream formStream)
+    public PDFormXObject(PDDocument document)
     {
-        super(formStream);
-        getCOSStream().setName(COSName.SUBTYPE, SUB_TYPE);
+        super(document, COSName.FORM);
     }
-    
+
     /**
      * This will get the form type, currently 1 is the only form type.
-     * 
      * @return The form type.
      */
     public int getFormType()
@@ -77,7 +101,6 @@ public class PDXObjectForm extends PDXOb
 
     /**
      * Set the form type.
-     * 
      * @param formType The new form type.
      */
     public void setFormType(int formType)
@@ -86,9 +109,9 @@ public class PDXObjectForm extends PDXOb
     }
 
     /**
-     * This will get the resources at this page and not look up the hierarchy. This attribute is inheritable, and
-     * findResources() should probably used. This will return null if no resources are available at this level.
-     * 
+     * This will get the resources at this page and not look up the hierarchy.
+     * This attribute is inheritable, and findResources() should probably used.
+     * This will return null if no resources are available at this level.
      * @return The resources at this level in the hierarchy.
      */
     public PDResources getResources()
@@ -98,13 +121,32 @@ public class PDXObjectForm extends PDXOb
         if (resources != null)
         {
             retval = new PDResources(resources);
+            // check for a recursion, see PDFBOX-1813
+            if (name != null)
+            {
+            	Map<String, PDXObject> xobjects = retval.getXObjects();
+            	if (xobjects != null && xobjects.containsKey(name))
+            	{
+            	    PDXObject xobject = xobjects.get(name);
+            	    if (xobject instanceof PDFormXObject)
+            	    {
+            	        int length1 = getCOSStream().getInt(COSName.LENGTH);
+                        int length2 = xobject.getCOSStream().getInt(COSName.LENGTH);
+                        // seems to be the same object
+            	        if (length1 == length2)
+            	        {
+            	            retval.removeXObject(name);
+                            LOG.debug("Removed XObjectForm "+name+" to avoid a recursion");
+            	        }
+            	    }
+            	}
+            }
         }
         return retval;
     }
 
     /**
      * This will set the resources for this page.
-     * 
      * @param resources The new resources for this page.
      */
     public void setResources(PDResources resources)
@@ -113,10 +155,10 @@ public class PDXObjectForm extends PDXOb
     }
 
     /**
-     * An array of four numbers in the form coordinate system (see below), giving the coordinates of the left, bottom,
-     * right, and top edges, respectively, of the form XObject's bounding box. These boundaries are used to clip the
-     * form XObject and to determine its size for caching.
-     * 
+     * An array of four numbers in the form coordinate system (see below),
+     * giving the coordinates of the left, bottom, right, and top edges, respectively,
+     * of the form XObject's bounding box.
+     * These boundaries are used to clip the form XObject and to determine its size for caching.
      * @return The BBox of the form.
      */
     public PDRectangle getBBox()
@@ -132,7 +174,6 @@ public class PDXObjectForm extends PDXOb
 
     /**
      * This will set the BBox (bounding box) for this form.
-     * 
      * @param bbox The new BBox for this form.
      */
     public void setBBox(PDRectangle bbox)
@@ -149,7 +190,6 @@ public class PDXObjectForm extends PDXOb
 
     /**
      * This will get the optional Matrix of an XObjectForm. It maps the form space into the user space
-     * 
      * @return the form matrix
      */
     public Matrix getMatrix()
@@ -171,7 +211,6 @@ public class PDXObjectForm extends PDXOb
 
     /**
      * Sets the optional Matrix entry for the form XObject.
-     * 
      * @param transform the transformation matrix
      */
     public void setMatrix(AffineTransform transform)
@@ -187,9 +226,9 @@ public class PDXObjectForm extends PDXOb
     }
 
     /**
-     * This will get the key of this XObjectForm in the structural parent tree. Required if the form XObject contains
-     * marked-content sequences that are structural content items.
-     * 
+     * This will get the key of this XObjectForm in the structural parent tree.
+     * Required if the form XObject contains marked-content sequences that are
+     * structural content items.
      * @return the integer key of the XObjectForm's entry in the structural parent tree
      */
     public int getStructParents()
@@ -199,12 +238,10 @@ public class PDXObjectForm extends PDXOb
 
     /**
      * This will set the key for this XObjectForm in the structural parent tree.
-     * 
      * @param structParent The new key for this XObjectForm.
      */
     public void setStructParents(int structParent)
     {
         getCOSStream().setInt(COSName.STRUCT_PARENTS, structParent);
     }
-
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/package.html
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/package.html?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/package.html (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/form/package.html Thu Feb 20 06:31:01 2014
@@ -0,0 +1,25 @@
+<!--
+ ! 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.
+ !-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This package deals with Form XObjects that are stored in a PDF document.
+</body>
+</html>

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CCITTFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CCITTFactory.java?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CCITTFactory.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CCITTFactory.java Thu Feb 20 06:31:01 2014
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.graphics.image;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.RandomAccess;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray;
+
+/**
+ * Factory for creating a PDImageXObject containing a CCITT Fax compressed TIFF image.
+ * @author Ben Litchfield
+ * @author Paul King
+ */
+public final class CCITTFactory
+{
+    private CCITTFactory()
+    {
+    }
+
+    /**
+     * Creates a new CCITT Fax compressed Image XObject from a TIFF file.
+     * @param document the document to create the image as part of.
+     * @param reader the random access TIFF file which contains a suitable CCITT compressed image
+     * @throws IOException if there is an error reading the TIFF data.
+     */
+    public static PDImageXObject createFromRandomAccess(PDDocument document, RandomAccess reader)
+            throws IOException
+    {
+        PDImageXObject pdImage = new PDImageXObject(document);
+
+        COSDictionary decodeParms = new COSDictionary();
+        COSDictionary dict = pdImage.getCOSStream();
+        extractFromTiff(reader, pdImage.getCOSStream().createFilteredStream(), decodeParms);
+
+        dict.setItem(COSName.FILTER, COSName.CCITTFAX_DECODE);
+        dict.setItem(COSName.SUBTYPE, COSName.IMAGE);
+        dict.setItem(COSName.TYPE, COSName.XOBJECT);
+        dict.setItem(COSName.DECODE_PARMS, decodeParms);
+
+        pdImage.setBitsPerComponent(1);
+        pdImage.setColorSpace(PDDeviceGray.INSTANCE);
+        pdImage.setWidth(decodeParms.getInt(COSName.COLUMNS));
+        pdImage.setHeight(decodeParms.getInt(COSName.ROWS));
+
+        return pdImage;
+    }
+
+    // extracts the CCITT stream from the TIFF file
+    private static void extractFromTiff(RandomAccess reader, OutputStream os, COSDictionary params)
+            throws IOException
+    {
+        try
+        {
+            // First check the basic tiff header
+            reader.seek(0);
+            char endianess = (char) reader.read();
+            if ((char) reader.read() != endianess)
+            {
+                throw new IOException("Not a valid tiff file");
+            }
+            // ensure that endianess is either M or I
+            if (endianess != 'M' && endianess != 'I')
+            {
+                throw new IOException("Not a valid tiff file");
+            }
+            int magicNumber = readshort(endianess, reader);
+            if (magicNumber != 42)
+            {
+                throw new IOException("Not a valid tiff file");
+            }
+
+            // Relocate to the first set of tags
+            reader.seek(readlong(endianess, reader));
+
+            int numtags = readshort(endianess, reader);
+
+            // The number 50 is somewhat arbitary, it just stops us load up junk from somewhere
+            // and tramping on
+            if (numtags > 50)
+            {
+                throw new IOException("Not a valid tiff file");
+            }
+
+            // Loop through the tags, some will convert to items in the parms dictionary
+            // Other point us to where to find the data stream
+            // The only parm which might change as a result of other options is K, so
+            // We'll deal with that as a special;
+
+            int k = -1000; // Default Non CCITT compression
+            int dataoffset = 0;
+            int datalength = 0;
+
+            for (int i = 0; i < numtags; i++)
+            {
+                int tag = readshort(endianess, reader);
+                int type = readshort(endianess, reader);
+                int count = readlong(endianess, reader);
+                int val = readlong(endianess, reader); // See note
+
+                // Note, we treated that value as a long. The value always occupies 4 bytes
+                // But it might only use the first byte or two. Depending on endianess we might
+                // need to correct.
+                // Note we ignore all other types, they are of little interest for PDFs/CCITT Fax
+                if (endianess == 'M')
+                {
+                    switch (type)
+                    {
+                    case 1:
+                    {
+                        val = val >> 24;
+                        break; // byte value
+                    }
+                    case 3:
+                    {
+                        val = val >> 16;
+                        break; // short value
+                    }
+                    case 4:
+                    {
+                        break; // long value
+                    }
+                    default:
+                    {
+                        // do nothing
+                    }
+                    }
+                }
+                switch (tag)
+                {
+                case 256:
+                {
+                    params.setInt(COSName.COLUMNS, val);
+                    break;
+                }
+                case 257:
+                {
+                    params.setInt(COSName.ROWS, val);
+                    break;
+                }
+                case 259:
+                {
+                    if (val == 4)
+                    {
+                        k = -1;
+                    }
+                    if (val == 3)
+                    {
+                        k = 0;
+                    }
+                    break; // T6/T4 Compression
+                }
+                case 262:
+                {
+                    if (val == 1)
+                    {
+                        params.setBoolean(COSName.BLACK_IS_1, true);
+                    }
+                    break;
+                }
+                case 273:
+                {
+                    if (count == 1)
+                    {
+                        dataoffset = val;
+                    }
+                    break;
+                }
+                case 279:
+                {
+                    if (count == 1)
+                    {
+                        datalength = val;
+                    }
+                    break;
+                }
+                case 292:
+                {
+                    if (val == 1)
+                    {
+                        k = 50; // T4 2D - arbitary K value
+                    }
+                    break;
+                }
+                case 324:
+                {
+                    if (count == 1)
+                    {
+                        dataoffset = val;
+                    }
+                    break;
+                }
+                case 325:
+                {
+                    if (count == 1)
+                    {
+                        datalength = val;
+                    }
+                    break;
+                }
+                default:
+                {
+                    // do nothing
+                }
+                }
+            }
+
+            if (k == -1000)
+            {
+                throw new IOException("First image in tiff is not CCITT T4 or T6 compressed");
+            }
+            if (dataoffset == 0)
+            {
+                throw new IOException("First image in tiff is not a single tile/strip");
+            }
+
+            params.setInt(COSName.K, k);
+
+            reader.seek(dataoffset);
+
+            byte[] buf = new byte[8192];
+            int amountRead = -1;
+            while ((amountRead = reader.read(buf, 0, Math.min(8192, datalength))) > 0)
+            {
+                datalength -= amountRead;
+                os.write(buf, 0, amountRead);
+            }
+
+        }
+        finally
+        {
+            os.close();
+        }
+    }
+
+    private static int readshort(char endianess, RandomAccess raf) throws IOException
+    {
+        if (endianess == 'I')
+        {
+            return raf.read() | (raf.read() << 8);
+        }
+        return (raf.read() << 8) | raf.read();
+    }
+
+    private static int readlong(char endianess, RandomAccess raf) throws IOException
+    {
+        if (endianess == 'I')
+        {
+            return raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24);
+        }
+        return (raf.read() << 24) | (raf.read() << 16) | (raf.read() << 8) | raf.read();
+    }
+}

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/ImageFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/ImageFactory.java?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/ImageFactory.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/ImageFactory.java Thu Feb 20 06:31:01 2014
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.graphics.image;
+
+import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray;
+import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.WritableRaster;
+
+/**
+ * An image factory.
+ *
+ * @author John Hewson
+ * @author Brigitte Mathiak
+ */
+class ImageFactory
+{
+    protected ImageFactory()
+    {
+    }
+
+    // sets Image XObject properties from an AWT buffered image
+    protected static void setPropertiesFromAWT(BufferedImage awtImage, PDImageXObject pdImage)
+    {
+        pdImage.setColorSpace(toPDColorSpace(awtImage.getColorModel().getColorSpace()));
+        pdImage.setBitsPerComponent(awtImage.getColorModel().getComponentSize(0));
+        pdImage.setHeight(awtImage.getHeight());
+        pdImage.setWidth(awtImage.getWidth());
+    }
+
+    // returns a PDColorSpace for a given AWT ColorSpace
+    protected static PDColorSpace toPDColorSpace(ColorSpace awtColorSpace)
+    {
+        if (awtColorSpace instanceof ICC_ColorSpace)
+        {
+            throw new UnsupportedOperationException("ICC color spaces not implemented");
+        }
+        else
+        {
+            switch (awtColorSpace.getType())
+            {
+                case ColorSpace.TYPE_RGB:  return PDDeviceRGB.INSTANCE;
+                case ColorSpace.TYPE_GRAY: return PDDeviceGray.INSTANCE;
+                case ColorSpace.TYPE_CMYK: return PDDeviceCMYK.INSTANCE;
+                default: throw new UnsupportedOperationException("color space not implemented: " +
+                        awtColorSpace.getType());
+            }
+        }
+    }
+
+    // returns the alpha channel of an image
+    protected static BufferedImage getAlphaImage(BufferedImage image)
+    {
+        if (!image.getColorModel().hasAlpha())
+        {
+            return null;
+        }
+
+        // extract the alpha information
+        WritableRaster alphaRaster = image.getAlphaRaster();
+        ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
+                false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+        return new BufferedImage(cm, alphaRaster, false, null);
+    }
+
+    // returns the color channels of an image
+    protected static BufferedImage getColorImage(BufferedImage image)
+    {
+        if (!image.getColorModel().hasAlpha())
+        {
+            return image;
+        }
+
+        if (image.getColorModel().getColorSpace().getType() != ColorSpace.TYPE_RGB)
+        {
+            throw new UnsupportedOperationException("only RGB color spaces are implemented");
+        }
+
+        // create an RGB image without alpha
+        BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(),
+                BufferedImage.TYPE_INT_RGB);
+
+        Graphics2D g = rgbImage.createGraphics();
+        g.setComposite(AlphaComposite.Src);
+        g.drawImage(image, 0, 0, null);
+
+        return rgbImage;
+    }
+}

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java Thu Feb 20 06:31:01 2014
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.graphics.image;
+
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.IOUtils;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.util.ImageIOUtil;
+import org.w3c.dom.Element;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+import javax.imageio.stream.ImageOutputStream;
+
+/**
+ * Factory for creating a PDImageXObject containing a JPEG compressed image.
+ * @author John Hewson
+ */
+public final class JPEGFactory extends ImageFactory
+{
+    private JPEGFactory()
+    {
+    }
+
+    /**
+     * Creates a new JPEG Image XObject from an input stream containing JPEG data.
+     * The input stream data will be preserved and embedded in the PDF file without modification.
+     * @param document the document where the image will be created
+     * @param stream a stream of JPEG data
+     * @return a new Image XObject
+     * @throws IOException if the input stream cannot be read
+     */
+    public static PDImageXObject createFromStream(PDDocument document, InputStream stream)
+            throws IOException
+    {
+        // create Image XObject from stream
+        PDImageXObject pdImage = new PDImageXObject(new PDStream(document, stream, true), null);
+
+        // add DCT filter
+        pdImage.getCOSStream().setItem(COSName.FILTER, COSName.DCT_DECODE);
+
+        // read image
+        ImageIO.setUseCache(false);
+        BufferedImage awtImage = ImageIO.read(stream);
+
+        // no alpha
+        if (awtImage.getColorModel().hasAlpha())
+        {
+            throw new UnsupportedOperationException("alpha channel not implemented");
+        }
+
+        // set properties (width, height, depth, color space, etc.)
+        setPropertiesFromAWT(awtImage, pdImage);
+
+        return pdImage;
+    }
+
+    /**
+     * Creates a new JPEG Image XObject from a Buffered Image.
+     * @param document the document where the image will be created
+     * @param image the buffered image to embed
+     * @return a new Image XObject
+     * @throws IOException if the JPEG data cannot be written
+     */
+    public static PDImageXObject createFromImage(PDDocument document, BufferedImage image)
+        throws IOException
+    {
+        return createFromImage(document, image, 0.75f);
+    }
+
+    /**
+     * Creates a new JPEG Image XObject from a Buffered Image and a given quality.
+     * @param document the document where the image will be created
+     * @param image the buffered image to embed
+     * @param quality the desired JPEG compression quality
+     * @return a new Image XObject
+     * @throws IOException if the JPEG data cannot be written
+     */
+    public static PDImageXObject createFromImage(PDDocument document, BufferedImage image,
+                                                 float quality) throws IOException
+    {
+        return createFromImage(document, image, quality, ImageIOUtil.DEFAULT_SCREEN_RESOLUTION);
+    }
+
+    /**
+     * Creates a new JPEG Image XObject from a Buffered Image, a given quality and DPI.
+     * @param document the document where the image will be created
+     * @param image the buffered image to embed
+     * @param quality the desired JPEG compression quality
+     * @param dpi the desired DPI (resolution) of the JPEG
+     * @return a new Image XObject
+     * @throws IOException if the JPEG data cannot be written
+     */
+    public static PDImageXObject createFromImage(PDDocument document, BufferedImage image,
+                                                 float quality, int dpi) throws IOException
+    {
+        return createJPEG(document, image, quality, dpi);
+    }
+
+    // Creates an Image XObject from a Buffered Image using JAI Image I/O
+    private static PDImageXObject createJPEG(PDDocument document, BufferedImage image,
+                                             float quality, int dpi) throws IOException
+    {
+        // extract alpha channel (if any)
+        BufferedImage awtColor = getColorImage(image);
+        BufferedImage awtAlpha = getAlphaImage(image);
+
+        // create XObject
+        PDImageXObject pdImage = new PDImageXObject(new PDStream(document), null);
+
+        // add DCT filter
+        COSDictionary dict = pdImage.getCOSStream();
+        pdImage.getCOSStream().setItem(COSName.FILTER, COSName.DCT_DECODE);
+
+        // alpha -> soft mask
+        if (awtAlpha != null)
+        {
+            PDImage xAlpha = JPEGFactory.createFromImage(document, awtAlpha, quality);
+            dict.setItem(COSName.SMASK, xAlpha);
+        }
+
+        // set properties (width, height, depth, color space, etc.)
+        setPropertiesFromAWT(awtColor, pdImage);
+
+        // encode to JPEG
+        OutputStream out = null;
+        ImageOutputStream ios = null;
+        ImageWriter imageWriter = null;
+        try
+        {
+            out = pdImage.getCOSStream().createFilteredStream();
+
+            // find JAI writer
+            imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next();
+            ios = ImageIO.createImageOutputStream(out);
+            imageWriter.setOutput(ios);
+
+            // add compression
+            JPEGImageWriteParam jpegParam = (JPEGImageWriteParam)imageWriter.getDefaultWriteParam();
+            jpegParam.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
+            jpegParam.setCompressionQuality(quality);
+
+            // add metadata
+            ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(image);
+            IIOMetadata data = imageWriter.getDefaultImageMetadata(imageTypeSpecifier, jpegParam);
+            Element tree = (Element)data.getAsTree("javax_imageio_jpeg_image_1.0");
+            Element jfif = (Element)tree.getElementsByTagName("app0JFIF").item(0);
+            jfif.setAttribute("Xdensity", Integer.toString(dpi));
+            jfif.setAttribute("Ydensity", Integer.toString(dpi));
+            jfif.setAttribute("resUnits", "1"); // 1 = dots/inch
+
+            // write
+            imageWriter.write(data, new IIOImage(image, null, null), jpegParam);
+        }
+        finally
+        {
+            // clean up
+            IOUtils.closeQuietly(out);
+            if (ios != null)
+            {
+                ios.close();
+            }
+            if (imageWriter != null)
+            {
+                imageWriter.dispose();
+            }
+        }
+
+        return pdImage;
+    }
+}

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImage.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImage.java?rev=1570086&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImage.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImage.java Thu Feb 20 06:31:01 2014
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+package org.apache.pdfbox.pdmodel.graphics.image;
+
+import java.awt.Paint;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.pdmodel.common.COSObjectable;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
+
+/**
+ * An image in a PDF document.
+ * @author John Hewson
+ */
+public interface PDImage extends COSObjectable
+{
+    /**
+     * Returns the content of this image as an AWT buffered image with an (A)RGB color space.
+     * @return content of this image as a buffered image.
+     * @throws IOException
+     */
+    public BufferedImage getImage() throws IOException;
+
+    /**
+     * Returns an ARGB image filled with the given paint and using this image as a mask.
+     * @param paint the paint to fill the visible portions of the image with
+     * @return a masked image filled with the given paint
+     * @throws IOException if the image cannot be read
+     * @throws IllegalStateException if the image is not a stencil.
+     */
+    public BufferedImage getStencilImage(Paint paint) throws IOException;
+
+    /**
+     * Returns a stream containing this image's data.
+     * @return stream containing the image data
+     * @throws IOException if the
+     */
+    public PDStream getStream() throws IOException;
+
+    /**
+     * Returns true if the image is a stencil mask.
+     * @return true if the image is a stencil mask.
+     */
+    public boolean isStencil();
+
+    /**
+     * Sets whether or not the image is a stencil.
+     * This corresponds to the {@code ImageMask} entry in the image stream's dictionary.
+     * @param isStencil True to make the image a stencil.
+     */
+    public void setStencil(boolean isStencil);
+
+    /**
+     * Returns bits per component of this image, or -1 if one has not been set.
+     * @return The number of bits per component.
+     */
+    public int getBitsPerComponent();
+
+    /**
+     * Set the number of bits per component.
+     * @param bitsPerComponent The number of bits per component.
+     */
+    public void setBitsPerComponent(int bitsPerComponent);
+
+    /**
+     * Returns the image's color space
+     * @return The color space for this image.
+     * @throws IOException If there is an error getting the color space.
+     */
+    public PDColorSpace getColorSpace() throws IOException;
+
+    /**
+     * Sets the color space for this image.
+     * @param colorSpace The color space for this image.
+     */
+    public void setColorSpace(PDColorSpace colorSpace);
+
+    /**
+     * Returns height of this image, or -1 if one has not been set.
+     * @return The height.
+     */
+    public int getHeight();
+
+    /**
+     * Sets the height of the image.
+     * @param height The height of the image.
+     */
+    public void setHeight(int height);
+
+    /**
+     * Returns the width of this image, or -1 if one has not been set.
+     * @return The width.
+     */
+    public int getWidth();
+
+    /**
+     * Sets the width of the image.
+     * @param width The width of the image.
+     */
+    public void setWidth(int width);
+
+    /**
+     * Sets the decode array.
+     * @param decode  the new decode array.
+     */
+    public void setDecode(COSArray decode);
+
+    /**
+     * Returns the decode array.
+     * @return the decode array.
+     */
+    public COSArray getDecode();
+}