如何将 ICC 添加到现有 PDF 文档

Pau*_*cer 5 java pdf pdfbox

我有一个使用 CMYK 颜色的现有 PDF 文档。它是使用我获得的特定 ICC 配置文件创建的。如果我在配置文件处于活动状态时打开文档,则颜色明显不同。据我使用各种工具所知,文档中没有嵌入 ICC 配置文件。我想做的是将 ICC 配置文件嵌入到 PDF 中,以便第三方可以使用正确的颜色打开和查看它。我的理解是,这可能与 PDF 格式有关,但我尝试过的似乎都不起作用。

我根据一些例子使用PDFBox编写了一个小程序,但似乎没有效果。我觉得我在某个地方错过了一步。

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>");
}

}
Run Code Online (Sandbox Code Playgroud)

我不是 Java 专家,但我确实设法让它运行,它似乎做了一些事情,但我仍然没有看到正确的颜色,并且使用 imagemagick 或 pdfinfo 没有迹象表明它有颜色配置文件。

我觉得我应该以某种方式表明文档颜色空间是基于 ICC 的,但我看不到任何明显的方法来使用 PDFBox API 来做到这一点。

任何帮助将不胜感激(即使被告知它不起作用!)

编辑:

我相信这正如所写的那样工作,它将所需的输出意图添加到文档中。然而,我也发现这不是我所需要的 - 我现在相信我需要它来将 /ICCBased 流添加到 PDF - 叹息。下面更新的代码基于此 stackoverflow 问题的更新的 createColorSpace 函数。

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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这段代码现在有一个异常:

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)
Run Code Online (Sandbox Code Playgroud)

在这行代码中:

cosArray.add(pdStream.getStream());
Run Code Online (Sandbox Code Playgroud)

我能看到这段代码和另一个答案之间的唯一区别是我正在加载一个现有的 PDF 文档,而不是创建一个新的空文档。

为了进行测试,我使用 Adob​​e 的 US Web(涂层)SWOP v2 icc 配置文件,但与我测试的任何配置文件都是相同的例外情况。根据我对阅读 PDFBox 源代码的理解,这不是配置文件的问题,而是从文档中读取流的问题(没有 /ICCBased 流,这是这个问题的全部要点:))

编辑2:如果与 PDFBox 1.8.10 一起使用,上面的代码实际上可以毫无异常地运行 - 显然我已经在 2.0.0 RC2 中链接了,但没有意识到它(完全是 Java 新手)。