PdfBox - PDColorSpaceFactory.createColorSpace(docu

2019-03-01 09:16发布

问题:

I'm trying to create a PDF which has a single image on a single page. The tricky part is to use a custom (defined in a separate file) CMYK color space.

I've tried to call

PDColorSpaceFactory.createColorSpace(document, iccColorSpace)

but keep getting nullpointerexception. I've managed to track the issue up to the constructor:

public PDICCBased( PDDocument doc )
{
    array = new COSArray();
    array.add( COSName.ICCBASED );
    array.add( new PDStream( doc ) );
}

The PDICCBased object has stream field and it's obviously not set. Thus when it's called at:

public static PDColorSpace createColorSpace( PDDocument doc, ColorSpace cs ) throws IOException
{
    PDColorSpace retval = null;
    if( cs.isCS_sRGB() )
    {
        retval = PDDeviceRGB.INSTANCE;
    }
    else if( cs instanceof ICC_ColorSpace )
    {
        ICC_ColorSpace ics = (ICC_ColorSpace)cs;
        PDICCBased pdCS = new PDICCBased( doc );
        retval = pdCS;
        COSArray ranges = new COSArray();
        for( int i=0; i<cs.getNumComponents(); i++ )
        {
            ranges.add( new COSFloat( ics.getMinValue( i ) ) );
            ranges.add( new COSFloat( ics.getMaxValue( i ) ) );
        }
        PDStream iccData = pdCS.getPDStream();
        OutputStream output = null;
        try
        {
            output = iccData.createOutputStream(); <<<<<<<<<-------------
            output.write( ics.getProfile().getData() );
        }
        finally
        {
            if( output != null )
            {
                output.close();
            }
        }
        pdCS.setNumberOfComponents( cs.getNumComponents() );
    }
    else
    {
        throw new IOException( "Not yet implemented:" + cs );
    }
    return retval;
}

A NullPointerException is thrown.

Am I missing something? Is there another\ better way to create PDF usinf CMYK color space?

Updated createColorSpace:

    public static PDColorSpace createColorSpace( PDDocument doc, ColorSpace cs ) throws IOException
{
    PDColorSpace retval = null;
    if( cs.isCS_sRGB() )
    {
        retval = PDDeviceRGB.INSTANCE;
    }
    else if( cs instanceof ICC_ColorSpace )
    {
        ICC_ColorSpace ics = (ICC_ColorSpace)cs;

        // CREATING MANUALLY THE COS ARR  ****************************
        COSArray cosArray = new COSArray();  
        cosArray.add(COSName.ICCBASED);
        PDStream pdStream = new PDStream(doc);
        cosArray.add(pdStream.getStream());

        // USING DIFFERENT CONSTRUTOR  *******************************
        PDICCBased pdCS = new PDICCBased( cosArray );
        retval = pdCS;
        COSArray ranges = new COSArray();
        for( int i=0; i<cs.getNumComponents(); i++ )
        {
            ranges.add( new COSFloat( ics.getMinValue( i ) ) );
            ranges.add( new COSFloat( ics.getMaxValue( i ) ) );
        }
        PDStream iccData = pdCS.getPDStream();
        OutputStream output = null;
        try
        {
            output = ((COSStream)iccData.getCOSObject()).createFilteredStream();
            output.write( ics.getProfile().getData() );
        }
        finally
        {
            if( output != null )
            {
                output.close();
            }
        }
        pdCS.setNumberOfComponents( cs.getNumComponents() );
    }
    else
    {
        throw new IOException( "Not yet implemented:" + cs );
    }
    return retval;
}

That did the trick from creating the colorSpace.

The creation of ColorSpace from a custom icc and applying it to a given image:

ICC_ColorSpace iccColorSpace = new ICC_ColorSpace(ICC_Profile.getInstance("C:\\...\\USWebCoatedSWOP.icc")); 
ColorConvertOp op = new ColorConvertOp(image.getColorModel().getColorSpace(), iccColorSpace, null);
image = op.filter(image, null);

The creation of the PDF:

PDDocument document = new PDDocument();

PDColorSpace colorSpace = createColorSpace(document, iccColorSpace);

PDPage blankPage = new PDPage(new PDRectangle(100, 100));
document.addPage(blankPage);

PDPageContentStream pdPageContentStream = new PDPageContentStream(document, blankPage);

PDXObjectImage pdxObjectImage = new PDPixelMap(document, image);
pdxObjectImage.setColorSpace(colorSpace);
pdPageContentStream.drawXObject(pdxObjectImage, 0, 0, imagePostScriptWidth, imagePostScriptHeight);
pdPageContentStream.close();

document.save(byteArrayOutputStream);
document.close();

Hope this helps.

回答1:

Ok, this seems to be a bug with the first constructor. You could use the second constructor, which uses a COSArray:

The PDF spec explains what to do:

"An ICCBased colour space shall be an array: [/ICCBased stream]"

So you need to create a COSArray, with COSName.ICCBASED as first element, and a COSStream with your ICC data in the second. Create a COSStream and write to it with createFilteredStream(). That COSArray you can use for the second constructor.

Alternatively, just use PDDeviceCMYK.INSTANCE as the colorspace :-)

Update: I have opened issue PDFBOX-2812 and it has been fixed. You can get a snapshot in a few hours here.



标签: java pdfbox