为PDF上的特定ColorSpace设置“overprint = true”(不是整个PDF页面)

时间:2017-07-21 20:21:52

标签: pdf pdfbox

我需要在“PDF”(不是整个PDF页面)的ColorSpace级别设置overprint = true。我正在尝试使用PDFBox解决这个问题。

同样,我想仅为特定的colorSpace应用叠印(参见下面示例代码中的If条件),但是graphicsState.setStrokingOverprintControl(true);似乎是为整个PDF页面(所有colorSpaces)设置叠印。

以下是示例代码。有谁遇到过这个问题?我错过了什么吗?

示例代码:

public static void fixPdfOverprint(String inputFilePath, String outputFilePath) throws IOException {
        final ByteArrayInputStream pdfStream = new ByteArrayInputStream(readFileIntoMemory(inputFilePath));
        try(PDDocument document = PDDocument.load(pdfStream)) {
            for (PDPage page : document.getDocumentCatalog().getPages()) {
                try(PDPageContentStream contentStream = createPageContentStream(document, page)) {
                    PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
                    PDResources pdResources = document.getDocumentCatalog().getPages().get(0).getResources();
                    for (COSName cosName : pdResources.getColorSpaceNames()) {
                         if(cosName.getName().equals("<my specific colorSpace>")) {
                           graphicsState.setStrokingOverprintControl(true); // Why this is setting for the entire page rathen than just this colorSpace. Btw - I confirmed that this if condition is correct.
                         }
                    }
                    contentStream.setGraphicsStateParameters(graphicsState);
                }
            }

            document.save(outputFilePath);
        }
    }

1 个答案:

答案 0 :(得分:1)

以下是一些仅适用于页面内容流的代码。它不处理xobject表单,模式和任何其他内容流。它基于源代码下载中的RemoveAllText.java示例,但在设置颜色空间后插入ExtGState的设置。请注意,您必须至少进行一次更改//可以看到TODO。

public static void main(String[] args) throws IOException
{
    if( args.length != 2 )
    {
        usage();
    }
    else
    {
        try (PDDocument document = PDDocument.load(new File(args[0])))
        {
            if (document.isEncrypted())
            {
                System.err.println(
                        "Error: Encrypted documents are not supported for this example.");
                System.exit(1);
            }
            for (PDPage page : document.getPages())
            {
                insertOverprint(page, document);
            }
            document.save(args[1]);
        }
    }
}

private static void insertOverprint(PDPage page, PDDocument document) throws IOException
{
    // non stroking overprint control true
    PDExtendedGraphicsState extGStateNonStrokingOverprintCtrlTrue = new PDExtendedGraphicsState();
    extGStateNonStrokingOverprintCtrlTrue.setNonStrokingOverprintControl(true);
    COSName nameExtGStateNonStrokingOverprintCtrlTrue = page.getResources().add(extGStateNonStrokingOverprintCtrlTrue);

    // stroking overprint control true
    PDExtendedGraphicsState extGStateStrokingOverprintCtrlTrue = new PDExtendedGraphicsState();
    extGStateStrokingOverprintCtrlTrue.setStrokingOverprintControl(true);
    COSName nameExtGStateStrokingOverprintCtrlTrue = page.getResources().add(extGStateStrokingOverprintCtrlTrue);

    // non stroking overprint control false
    PDExtendedGraphicsState extGStateNonStrokingOverprintCtrlFalse = new PDExtendedGraphicsState();
    extGStateNonStrokingOverprintCtrlFalse.setNonStrokingOverprintControl(false);
    COSName nameExtGStateNonStrokingOverprintCtrlFalse = page.getResources().add(extGStateNonStrokingOverprintCtrlFalse);

    // stroking overprint control false
    PDExtendedGraphicsState extGStateStrokingOverprintCtrlFalse = new PDExtendedGraphicsState();
    extGStateStrokingOverprintCtrlFalse.setStrokingOverprintControl(false);
    COSName nameExtGStateStrokingOverprintCtrlFalse = page.getResources().add(extGStateStrokingOverprintCtrlFalse);

    PDFStreamParser parser = new PDFStreamParser(page);
    List<Object> newTokens = new ArrayList<>();
    Object token = parser.parseNextToken();
    while (token != null)
    {
        if (token instanceof Operator && !newTokens.isEmpty())
        {
            String opname = ((Operator) token).getName();
            Object lastToken = newTokens.get(newTokens.size() - 1);
            // check whether this is an operator that sets colorspace and was preceded by a name
            if (lastToken instanceof COSName && !"Pattern".equals(((COSName) lastToken).getName()) && 
                    ("CS".equals(opname.toUpperCase()) || "SCN".equals(opname.toUpperCase())))
            {
                // get last item = argument = colorspace name
                COSName name = (COSName) lastToken;
                System.out.println(name.getName() + " " + opname);
                newTokens.add(token);

                if (true) // TODO !here! add code to check whether this is the correct colorspace name
                {
                    if (Character.isUpperCase(opname.charAt(0)))
                    {
                        // stroking
                        newTokens.add(nameExtGStateStrokingOverprintCtrlTrue);
                    }
                    else
                    {
                        // nonstroking
                        newTokens.add(nameExtGStateNonStrokingOverprintCtrlTrue);
                    }
                }
                else
                {
                    if (opname.contains("S"))
                    {
                        // stroking
                        newTokens.add(nameExtGStateStrokingOverprintCtrlFalse);
                    }
                    else
                    {
                        // nonstroking
                        newTokens.add(nameExtGStateNonStrokingOverprintCtrlFalse);
                    }
                }
                // Set parameters from graphics state parameter dictionary
                newTokens.add(Operator.getOperator("gs"));
                token = parser.parseNextToken();
                continue;
            }
            // check all operators that implicitely set a colorspace
            else if ("G".equals(opname.toUpperCase()) || "RG".equals(opname.toUpperCase()) || "K".equals(opname.toUpperCase()))
            {
                newTokens.add(token);
                if (Character.isUpperCase(opname.charAt(0)))
                {
                    // stroking
                    newTokens.add(nameExtGStateStrokingOverprintCtrlFalse);
                }
                else
                {
                    // nonstroking
                    newTokens.add(nameExtGStateNonStrokingOverprintCtrlFalse);
                }
                // Set parameters from graphics state parameter dictionary
                newTokens.add(Operator.getOperator("gs"));
                token = parser.parseNextToken();
                continue;
            }
        }
        newTokens.add(token);
        token = parser.parseNextToken();
    }

    PDStream newContents = new PDStream(document);
    try (OutputStream out = newContents.createOutputStream(COSName.FLATE_DECODE))
    {
        ContentStreamWriter writer = new ContentStreamWriter(out);
        writer.writeTokens(newTokens);
    }
    page.setContents(newContents);
}

/**
 * This will print the usage for this document.
 */
private static void usage()
{
    System.err.println("Usage: java " + InsertOverprint.class.getName() + " <input-pdf> <output-pdf>");
}

更多解释:sc,scn,SC和SCN是设置描边或非描边颜色的操作符。大写操作符用于描边操作,小写操作符用于非描边操作符。在内容流中,颜色空间的名称位于运算符之前。我排除了“图案”颜色,因为它不是真正的颜色空间。

更新25.7.2017:RemoveAllTexts example(我用作此问题的起点)已得到改进,现在它不仅处理页面和xobject表单,还处理模式。要根据此处的操作对其进行修改,请查看createTokensWithoutText()