How do I add an ICC to an existing PDF document

别等时光非礼了梦想. 提交于 2019-12-22 08:26:22

问题


I have an existing PDF document that is using CMYK colors. It was created using a specific ICC profile, which I have obtained. The colors are obviously different if I open the document with the profile active than without. From what I can tell using a variety of tools, there is no ICC profile embedded in the document. What I would like to do is embed the ICC profile in the PDF so that it can be opened and viewed with the correct colors by third parties. My understanding is that this is possible to do with the PDF format, but nothing I have tried seems to work.

I wrote a small program using PDFBox based on looking at some examples, but it seems to have no effect. I feel like I am missing a step somewhere.

package com.mapsherpa.tools.addicc;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;

import java.io.FileInputStream;
import java.io.IOException;

public class AddICC {

public AddICC() {
    // TODO Auto-generated constructor stub
}

public static void main(String[] args) {
    AddICC app = new AddICC();
    try {
        if( args.length != 3) {
            app.usage();
        } else {
            app.doIt(args[0], args[1], args[2]);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

private void doIt(String input, String output, String icc) throws IOException {
    // TODO Auto-generated method stub

    System.out.printf("Adding %s to %s and saving as %s\n", icc, input, output);

    PDDocument doc = null;
    try
    {
        File file = new File(input);
        doc = PDDocument.load(file);
        PDDocumentCatalog cat = doc.getDocumentCatalog();
        PDMetadata metadata = new PDMetadata(doc);
        cat.setMetadata(metadata);
        InputStream colorProfile = new FileInputStream(icc);
        PDOutputIntent oi = new PDOutputIntent(doc, colorProfile);
        oi.setInfo("SWOP (Coated), 20%, GCR, None");
        oi.setOutputCondition("SWOP (Coated), 20%, GCR, None");
        oi.setOutputConditionIdentifier("SWOP (Coated), 20%, GCR, None");
        oi.setRegistryName("http://www.color.org");
        cat.addOutputIntent(oi);
        doc.save(output);
        System.out.println("Finished adding color profile");
    } 
    catch (Exception e)
    {
        System.out.println("Exception processing color profile");
        e.printStackTrace();
    }
    finally
    {
        if (doc != null) {
            doc.close();
        }
    }

}

private void usage() {
    // TODO Auto-generated method stub
    System.err.println("Usage: " + this.getClass().getName() + " <input-file> <output-file> <icc-file>");
}

}

I'm not a Java expert but I did manage to get this to run and it seems to do something but I still am not seeing the correct colors and there is no indication using imagemagick or pdfinfo that it has a color profile.

I feel like somehow I should be indicating that the document color space is ICCBased but I can't see any obvious way to do that using the PDFBox API.

Any help would be appreciated (even being told that it won't work!)

EDIT:

I believe that this is working as written in that it adds the required output intent to the document. However, I have also discovered that this is not what I need - I now believe that I need it to add an /ICCBased stream to the PDF - sigh. The updated code below is based on this stackoverflow question's updated createColorSpace function.

private 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;
}

private void doIt(String input, String output, String icc) throws IOException {
    // TODO Auto-generated method stub

    System.out.printf("Adding %s to %s and saving as %s\n", icc, input, output);

    PDDocument doc = null;
    try
    {
        File file = new File(input);
        doc = PDDocument.load(file);

        ICC_ColorSpace iccColorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(icc));

        PDColorSpace colorSpace = createColorSpace(doc, iccColorSpace);

        doc.save(output);
        System.out.println("Finished adding color profile");
    } 
    catch (Exception e)
    {
        System.out.println("Exception processing color profile");
        e.printStackTrace();
    }
    finally
    {
        if (doc != null) {
            doc.close();
        }
    }

}

This code now has an exception:

java.io.IOException: Unknown color space number of components:-1
at org.apache.pdfbox.pdmodel.graphics.color.PDICCBased.getAlternateColorSpace(PDICCBased.java:269)
at org.apache.pdfbox.pdmodel.graphics.color.PDICCBased.loadICCProfile(PDICCBased.java:151)
at org.apache.pdfbox.pdmodel.graphics.color.PDICCBased.<init>(PDICCBased.java:89)
at com.mapsherpa.tools.addicc.AddICC.createColorSpace(AddICC.java:65)
at com.mapsherpa.tools.addicc.AddICC.doIt(AddICC.java:109)
at com.mapsherpa.tools.addicc.AddICC.main(AddICC.java:39)

at this line of code:

cosArray.add(pdStream.getStream());

The only difference I can see between this code and the other answer is that I am loading an existing PDF document rather than creating a new empty one.

For testing, I'm using the US Web (Coated) SWOP v2 icc profile from Adobe, but it is the same exception with any profile I test. From my understanding of reading the PDFBox source, it isn't a problem with the profile but with reading the stream from the document (which doesn't have an /ICCBased stream, the whole point of this question :))

EDIT 2: the code above does actually run without exceptions if used with PDFBox 1.8.10 - apparently I had linked in 2.0.0 RC2 without realizing it (total Java newbie).

来源:https://stackoverflow.com/questions/34138201/how-do-i-add-an-icc-to-an-existing-pdf-document

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!