org.apache.pdfbox.pdmodel.graphics.color.PDICCBased Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdfbox Show documentation
Show all versions of pdfbox Show documentation
The Apache PDFBox library is an open source Java tool for working with PDF documents.
/*
* 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.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.COSFloat;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
import org.apache.pdfbox.pdmodel.common.PDRange;
import org.apache.pdfbox.pdmodel.common.PDStream;
import java.awt.Color;
import java.awt.Transparency;
import java.awt.color.CMMException;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.color.ProfileDataException;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* This class represents a ICC profile color space.
*
* @author Ben Litchfield
* @version $Revision: 1.6 $
*/
public class PDICCBased extends PDColorSpace
{
/**
* Log instance.
*/
private static final Log LOG = LogFactory.getLog(PDICCBased.class);
/**
* The name of this color space.
*/
public static final String NAME = "ICCBased";
//private COSArray array;
private PDStream stream;
/**
* Number of color components.
*/
private int numberOfComponents = -1;
/**
* Default constructor, creates empty stream.
*
* @param doc The document to store the icc data.
*/
public PDICCBased( PDDocument doc )
{
array = new COSArray();
array.add( COSName.ICCBASED );
stream = new PDStream(doc);
array.add(stream);
}
/**
* Constructor.
*
* @param iccArray The ICC stream object.
*/
public PDICCBased( COSArray iccArray )
{
array = iccArray;
stream = new PDStream( (COSStream)iccArray.getObject( 1 ) );
}
/**
* This will return the name of the color space.
*
* @return The name of the color space.
*/
public String getName()
{
return NAME;
}
/**
* Convert this standard java object to a COS object.
*
* @return The cos object that matches this Java object.
*/
@Override
public COSBase getCOSObject()
{
return array;
}
/**
* Get the pd stream for this icc color space.
*
* @return Get the stream for this icc based color space.
*/
public PDStream getPDStream()
{
return stream;
}
/**
* 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
{
InputStream profile = null;
ColorSpace cSpace = null;
try
{
profile = stream.createInputStream();
ICC_Profile iccProfile = ICC_Profile.getInstance( profile );
cSpace = new ICC_ColorSpace( iccProfile );
// there maybe a ProfileDataException or a CMMException as there
// are some issues when loading ICC_Profiles, see PDFBOX-1295
// Try to create a color as test ...
new Color(cSpace,new float[getNumberOfComponents()],1f);
}
catch (RuntimeException e)
{
if (e instanceof ProfileDataException
|| e instanceof CMMException
|| e instanceof IllegalArgumentException)
{
// we are using an alternate colorspace as fallback
List alternateCSList = getAlternateColorSpaces();
PDColorSpace alternate = (PDColorSpace) alternateCSList.get(0);
LOG.error("Can't read ICC-profile, using alternate colorspace instead: " + alternate);
cSpace = alternate.getJavaColorSpace();
}
else
{
throw e;
}
}
finally
{
if( profile != null )
{
profile.close();
}
}
return cSpace;
}
/**
* 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
{
int[] nbBits;
int numOfComponents = getNumberOfComponents();
switch (numOfComponents)
{
case 1:
// DeviceGray
nbBits = new int[]{ bpc };
break;
case 3:
// DeviceRGB
nbBits = new int[]{ bpc, bpc, bpc };
break;
case 4:
// DeviceCMYK
nbBits = new int[]{ bpc, bpc, bpc, bpc };
break;
default:
throw new IOException( "Unknown colorspace number of components:" + numOfComponents );
}
ComponentColorModel componentColorModel =
new ComponentColorModel( getJavaColorSpace(),
nbBits,
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE );
return componentColorModel;
}
/**
* This will return the number of color components. As of PDF 1.4 this will
* be 1,3,4.
*
* @return The number of components in this color space.
*/
public int getNumberOfComponents()
{
if (numberOfComponents < 0)
{
numberOfComponents = stream.getStream().getInt(COSName.N);
}
return numberOfComponents;
}
/**
* This will set the number of color components.
*
* @param n The number of color components.
*/
public void setNumberOfComponents( int n )
{
numberOfComponents = n;
stream.getStream().setInt( COSName.N, n );
}
/**
* This will return a list of alternate color spaces(PDColorSpace) if the display application
* does not support this icc stream.
*
* @return A list of alternate color spaces.
*
* @throws IOException If there is an error getting the alternate color spaces.
*/
public List getAlternateColorSpaces() throws IOException
{
COSBase alternate = stream.getStream().getDictionaryObject( COSName.ALTERNATE );
COSArray alternateArray = null;
if( alternate == null )
{
alternateArray = new COSArray();
int numComponents = getNumberOfComponents();
COSName csName = null;
if( numComponents == 1 )
{
csName = COSName.DEVICEGRAY;
}
else if( numComponents == 3 )
{
csName = COSName.DEVICERGB;
}
else if( numComponents == 4 )
{
csName = COSName.DEVICECMYK;
}
else
{
throw new IOException( "Unknown colorspace number of components:" + numComponents );
}
alternateArray.add( csName );
}
else
{
if( alternate instanceof COSArray )
{
alternateArray = (COSArray)alternate;
}
else if( alternate instanceof COSName )
{
alternateArray = new COSArray();
alternateArray.add( alternate );
}
else
{
throw new IOException( "Error: expected COSArray or COSName and not " +
alternate.getClass().getName() );
}
}
List retval = new ArrayList();
retval.add( PDColorSpaceFactory.createColorSpace( alternateArray ) );
return new COSArrayList( retval, alternateArray );
}
/**
* This will set the list of alternate color spaces. This should be a list
* of PDColorSpace objects.
*
* @param list The list of colorspace objects.
*/
public void setAlternateColorSpaces( List list )
{
COSArray altArray = null;
if( list != null )
{
altArray = COSArrayList.converterToCOSArray( list );
}
stream.getStream().setItem( COSName.ALTERNATE, altArray );
}
/**
* Returns the range for a certain component number.
* This will never return null.
* If it is not present then the range 0..1 will be returned.
* @param n the component number to get the range for
* @return the range for this component
*/
public PDRange getRangeForComponent(int n)
{
COSArray rangeArray = (COSArray) stream.getStream().getDictionaryObject(COSName.RANGE);
if (rangeArray == null || rangeArray.size() < getNumberOfComponents() * 2)
{
return new PDRange(); // 0..1
}
return new PDRange(rangeArray, n);
}
/**
* This will set the a range for this color space.
*
* @param range The new range for the a component.
* @param n The component to set the range for.
*/
public void setRangeForComponent(PDRange range, int n)
{
COSArray rangeArray = (COSArray) stream.getStream().getDictionaryObject(COSName.RANGE);
if (rangeArray == null)
{
rangeArray = new COSArray();
stream.getStream().setItem(COSName.RANGE, rangeArray);
}
// extend range array with default values if needed
while (rangeArray.size() < (n + 1) * 2)
{
rangeArray.add(new COSFloat(0));
rangeArray.add(new COSFloat(1));
}
rangeArray.set(n*2, new COSFloat(range.getMin()));
rangeArray.set(n*2+1, new COSFloat(range.getMax()));
}
/**
* This will get the metadata stream for this object. Null if there is no
* metadata stream.
*
* @return The metadata stream, if it exists.
*/
public COSStream getMetadata()
{
return (COSStream)stream.getStream().getDictionaryObject( COSName.METADATA );
}
/**
* This will set the metadata stream that is associated with this color space.
*
* @param metadata The new metadata stream.
*/
public void setMetadata( COSStream metadata )
{
stream.getStream().setItem( COSName.METADATA, metadata );
}
// Need more info on the ICCBased ones ... Array contains very little.
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
return getName() + "{numberOfComponents: " + getNumberOfComponents() + "}";
}
}