为 PDF(不是整个 PDF 页面)上的特定 ColorSpace 设置 "overprint=true"
Setting "overprint=true" for a specific ColorSpace on PDF (not the entire PDF Page)
我需要在 "PDF" 的 ColorSpace 级别设置 overprint=true(不是针对整个 PDF 页面)。我正在尝试使用 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);
}
}
下面是一些仅适用于页面内容流的代码。它不处理 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是设置描边或非描边颜色的运算符。大写运算符用于描边,小写运算符用于非描边。在内容流中,颜色空间的名称在运算符之前。我排除了 "Pattern" 颜色,因为它不是真正的色彩空间。
2017 年 7 月 25 日更新:RemoveAllTexts example(我将其用作此问题的起点)已得到改进,现在它不仅可以处理页面和 xobject 表单,还可以处理模式。要根据此处所做的修改它,请查看 createTokensWithoutText()
。
我需要在 "PDF" 的 ColorSpace 级别设置 overprint=true(不是针对整个 PDF 页面)。我正在尝试使用 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);
}
}
下面是一些仅适用于页面内容流的代码。它不处理 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是设置描边或非描边颜色的运算符。大写运算符用于描边,小写运算符用于非描边。在内容流中,颜色空间的名称在运算符之前。我排除了 "Pattern" 颜色,因为它不是真正的色彩空间。
2017 年 7 月 25 日更新:RemoveAllTexts example(我将其用作此问题的起点)已得到改进,现在它不仅可以处理页面和 xobject 表单,还可以处理模式。要根据此处所做的修改它,请查看 createTokensWithoutText()
。