尽管删除了 PDAnnotation,但文本显示为蓝色
Text displayed in blue although PDAnnotation removed
我们有一个要求,需要在某些匹配的条件检查中删除注释。当我执行 allPageAnnotationsList.remove(annotationTobeRemoved)
语句时,PDAnnotaion 被删除。
但相应的文字仍然只显示为蓝色。我怎样才能将文本颜色更新为正常(黑色)?
本来我以为你要求将页面上的所有非黑色文本都更改为黑色。这导致了我原来的答案,现在是第一部分 'Updating All Text to Black'。然后您阐明了您只想将已删除注释区域中的文本设为黑色。这显示在第二部分 'Updating Text in Areas to Black'.
将所有文本更新为黑色
首先,正如 Tilman 在评论中所述,删除 link 注释通常只会删除 link 的交互性,但会删除 link 注释区域中的文本保持原样。如果要将文本颜色更新为正常(黑色),因此,您必须添加第二个步骤并操作静态页面内容中的颜色。
静态页面内容由改变图形状态或绘制内容的指令流定义。用于绘图的颜色是图形状态的一部分,由显式颜色设置指令设置。因此,人们可能认为您可以简单地通过选择 normal(black).
的指令替换所有颜色设置指令。
不幸的是,这并不容易,因为也可能会更改颜色以绘制其他内容。例如。在您的文档中,一开始整个页面都是白色的;如果您在该填充指令之前替换了颜色设置指令,您的整个页面将是黑色的。不完全是你想要的。
要将文本颜色更新为正常(黑色)但不更改其他颜色,因此,您必须考虑要更改的指令的上下文。
PDFBox 解析框架可以在这方面为您提供帮助,遍历内容流并跟踪图形状态。
此外,基于该框架,在 , the PdfContentStreamEditor
中创建了一个通用的内容流编辑器助手 class。 (有关详细信息和示例用途,请参阅该答案。)现在您只需要为您的用例自定义它,例如像这样:
PDDocument document = ...;
for (PDPage page : document.getDocumentCatalog().getPages()) {
PdfContentStreamEditor editor = new PdfContentStreamEditor(document, page) {
@Override
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
String operatorString = operator.getName();
if (TEXT_SHOWING_OPERATORS.contains(operatorString)) {
if (currentlyReplacedColor == null)
{
PDColor currentFillColor = getGraphicsState().getNonStrokingColor();
if (!isBlack(currentFillColor))
{
currentlyReplacedColor = currentFillColor;
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, GRAY_BLACK_VALUES);
}
}
} else if (currentlyReplacedColor != null) {
PDColorSpace replacedColorSpace = currentlyReplacedColor.getColorSpace();
List<COSBase> replacedColorValues = new ArrayList<>();
for (float f : currentlyReplacedColor.getComponents())
replacedColorValues.add(new COSFloat(f));
if (replacedColorSpace instanceof PDDeviceCMYK)
super.write(contentStreamWriter, SET_NON_STROKING_CMYK, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceGray)
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceRGB)
super.write(contentStreamWriter, SET_NON_STROKING_RGB, replacedColorValues);
else {
//TODO
}
currentlyReplacedColor = null;
}
super.write(contentStreamWriter, operator, operands);
}
PDColor currentlyReplacedColor = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
final Operator SET_NON_STROKING_CMYK = Operator.getOperator("k");
final Operator SET_NON_STROKING_RGB = Operator.getOperator("rg");
final Operator SET_NON_STROKING_GRAY = Operator.getOperator("g");
final List<COSBase> GRAY_BLACK_VALUES = Arrays.asList(COSInteger.ZERO);
};
editor.processPage(page);
}
document.save("withBlackText.pdf");
(ChangeTextColor 测试 testMakeTextBlackTestAfterRemovingAnnotation
)
这里检查当前指令是否为文字绘制指令。如果是并且当前颜色尚未被替换,我们检查当前颜色是否已经是黑色。如果不是黑色,我们存储它,并添加一条指令,将当前填充颜色替换为黑色。
否则,即如果当前指令不是文本绘制指令,我们检查当前颜色是否已经被黑色替换。如果有,我们恢复原来的颜色。
要检查给定颜色是否偏黑,我们使用以下辅助方法。
static boolean isBlack(PDColor pdColor) {
PDColorSpace pdColorSpace = pdColor.getColorSpace();
float[] components = pdColor.getComponents();
if (pdColorSpace instanceof PDDeviceCMYK)
return (components[0] > .9f && components[1] > .9f && components[2] > .9f) || components[3] > .9f;
else if (pdColorSpace instanceof PDDeviceGray)
return components[0] < .1f;
else if (pdColorSpace instanceof PDDeviceRGB)
return components[0] < .1f && components[1] < .1f && components[2] < .1f;
else
return false;
}
(ChangeTextColor辅助方法)
将区域中的文本更新为黑色
您在评论中阐明了您只希望已删除注释区域中的文本变为黑色。
为此,您必须收集您删除的注释的矩形,然后在切换颜色之前检查位置是否在这些矩形之一内。
这可以通过如下扩展上面的代码来完成。在这里,我只删除所有其他注释并收集它们的矩形以便稍后检查它们。此外,我重写 PDFStreamEngine
方法 showText(byte[])
以存储当前文本绘制指令中显示的文本位置。
PDDocument document = ...;
for (PDPage page : document.getDocumentCatalog().getPages()) {
List<PDRectangle> areas = new ArrayList<>();
// Remove every other annotation, collect their areas
List<PDAnnotation> annotations = new ArrayList<>();
boolean remove = true;
for (PDAnnotation annotation : page.getAnnotations()) {
if (remove)
areas.add(annotation.getRectangle());
else
annotations.add(annotation);
remove = !remove;
}
page.setAnnotations(annotations);
PdfContentStreamEditor editor = new PdfContentStreamEditor(document, page) {
@Override
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
String operatorString = operator.getName();
if (TEXT_SHOWING_OPERATORS.contains(operatorString) && isInAreas()) {
if (currentlyReplacedColor == null)
{
PDColor currentFillColor = getGraphicsState().getNonStrokingColor();
if (!isBlack(currentFillColor))
{
currentlyReplacedColor = currentFillColor;
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, GRAY_BLACK_VALUES);
}
}
} else if (currentlyReplacedColor != null) {
PDColorSpace replacedColorSpace = currentlyReplacedColor.getColorSpace();
List<COSBase> replacedColorValues = new ArrayList<>();
for (float f : currentlyReplacedColor.getComponents())
replacedColorValues.add(new COSFloat(f));
if (replacedColorSpace instanceof PDDeviceCMYK)
super.write(contentStreamWriter, SET_NON_STROKING_CMYK, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceGray)
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceRGB)
super.write(contentStreamWriter, SET_NON_STROKING_RGB, replacedColorValues);
else {
//TODO
}
currentlyReplacedColor = null;
}
super.write(contentStreamWriter, operator, operands);
before = null;
after = null;
}
PDColor currentlyReplacedColor = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
final Operator SET_NON_STROKING_CMYK = Operator.getOperator("k");
final Operator SET_NON_STROKING_RGB = Operator.getOperator("rg");
final Operator SET_NON_STROKING_GRAY = Operator.getOperator("g");
final List<COSBase> GRAY_BLACK_VALUES = Arrays.asList(COSInteger.ZERO);
@Override
protected void showText(byte[] string) throws IOException {
Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
if (before == null)
before = getTextMatrix().multiply(ctm);
super.showText(string);
after = getTextMatrix().multiply(ctm);
}
Matrix before = null;
Matrix after = null;
boolean isInAreas() {
return isInAreas(before) || isInAreas(after);
}
boolean isInAreas(Matrix m) {
return m != null && areas.stream().anyMatch(rect -> rect.contains(m.getTranslateX(), m.getTranslateY()));
}
};
editor.processPage(page);
}
document.save("WithoutSomeAnnotation-withBlackTextThere.pdf");
我们有一个要求,需要在某些匹配的条件检查中删除注释。当我执行 allPageAnnotationsList.remove(annotationTobeRemoved)
语句时,PDAnnotaion 被删除。
但相应的文字仍然只显示为蓝色。我怎样才能将文本颜色更新为正常(黑色)?
本来我以为你要求将页面上的所有非黑色文本都更改为黑色。这导致了我原来的答案,现在是第一部分 'Updating All Text to Black'。然后您阐明了您只想将已删除注释区域中的文本设为黑色。这显示在第二部分 'Updating Text in Areas to Black'.
将所有文本更新为黑色
首先,正如 Tilman 在评论中所述,删除 link 注释通常只会删除 link 的交互性,但会删除 link 注释区域中的文本保持原样。如果要将文本颜色更新为正常(黑色),因此,您必须添加第二个步骤并操作静态页面内容中的颜色。
静态页面内容由改变图形状态或绘制内容的指令流定义。用于绘图的颜色是图形状态的一部分,由显式颜色设置指令设置。因此,人们可能认为您可以简单地通过选择 normal(black).
的指令替换所有颜色设置指令。不幸的是,这并不容易,因为也可能会更改颜色以绘制其他内容。例如。在您的文档中,一开始整个页面都是白色的;如果您在该填充指令之前替换了颜色设置指令,您的整个页面将是黑色的。不完全是你想要的。
要将文本颜色更新为正常(黑色)但不更改其他颜色,因此,您必须考虑要更改的指令的上下文。
PDFBox 解析框架可以在这方面为您提供帮助,遍历内容流并跟踪图形状态。
此外,基于该框架,在 PdfContentStreamEditor
中创建了一个通用的内容流编辑器助手 class。 (有关详细信息和示例用途,请参阅该答案。)现在您只需要为您的用例自定义它,例如像这样:
PDDocument document = ...;
for (PDPage page : document.getDocumentCatalog().getPages()) {
PdfContentStreamEditor editor = new PdfContentStreamEditor(document, page) {
@Override
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
String operatorString = operator.getName();
if (TEXT_SHOWING_OPERATORS.contains(operatorString)) {
if (currentlyReplacedColor == null)
{
PDColor currentFillColor = getGraphicsState().getNonStrokingColor();
if (!isBlack(currentFillColor))
{
currentlyReplacedColor = currentFillColor;
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, GRAY_BLACK_VALUES);
}
}
} else if (currentlyReplacedColor != null) {
PDColorSpace replacedColorSpace = currentlyReplacedColor.getColorSpace();
List<COSBase> replacedColorValues = new ArrayList<>();
for (float f : currentlyReplacedColor.getComponents())
replacedColorValues.add(new COSFloat(f));
if (replacedColorSpace instanceof PDDeviceCMYK)
super.write(contentStreamWriter, SET_NON_STROKING_CMYK, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceGray)
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceRGB)
super.write(contentStreamWriter, SET_NON_STROKING_RGB, replacedColorValues);
else {
//TODO
}
currentlyReplacedColor = null;
}
super.write(contentStreamWriter, operator, operands);
}
PDColor currentlyReplacedColor = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
final Operator SET_NON_STROKING_CMYK = Operator.getOperator("k");
final Operator SET_NON_STROKING_RGB = Operator.getOperator("rg");
final Operator SET_NON_STROKING_GRAY = Operator.getOperator("g");
final List<COSBase> GRAY_BLACK_VALUES = Arrays.asList(COSInteger.ZERO);
};
editor.processPage(page);
}
document.save("withBlackText.pdf");
(ChangeTextColor 测试 testMakeTextBlackTestAfterRemovingAnnotation
)
这里检查当前指令是否为文字绘制指令。如果是并且当前颜色尚未被替换,我们检查当前颜色是否已经是黑色。如果不是黑色,我们存储它,并添加一条指令,将当前填充颜色替换为黑色。
否则,即如果当前指令不是文本绘制指令,我们检查当前颜色是否已经被黑色替换。如果有,我们恢复原来的颜色。
要检查给定颜色是否偏黑,我们使用以下辅助方法。
static boolean isBlack(PDColor pdColor) {
PDColorSpace pdColorSpace = pdColor.getColorSpace();
float[] components = pdColor.getComponents();
if (pdColorSpace instanceof PDDeviceCMYK)
return (components[0] > .9f && components[1] > .9f && components[2] > .9f) || components[3] > .9f;
else if (pdColorSpace instanceof PDDeviceGray)
return components[0] < .1f;
else if (pdColorSpace instanceof PDDeviceRGB)
return components[0] < .1f && components[1] < .1f && components[2] < .1f;
else
return false;
}
(ChangeTextColor辅助方法)
将区域中的文本更新为黑色
您在评论中阐明了您只希望已删除注释区域中的文本变为黑色。
为此,您必须收集您删除的注释的矩形,然后在切换颜色之前检查位置是否在这些矩形之一内。
这可以通过如下扩展上面的代码来完成。在这里,我只删除所有其他注释并收集它们的矩形以便稍后检查它们。此外,我重写 PDFStreamEngine
方法 showText(byte[])
以存储当前文本绘制指令中显示的文本位置。
PDDocument document = ...;
for (PDPage page : document.getDocumentCatalog().getPages()) {
List<PDRectangle> areas = new ArrayList<>();
// Remove every other annotation, collect their areas
List<PDAnnotation> annotations = new ArrayList<>();
boolean remove = true;
for (PDAnnotation annotation : page.getAnnotations()) {
if (remove)
areas.add(annotation.getRectangle());
else
annotations.add(annotation);
remove = !remove;
}
page.setAnnotations(annotations);
PdfContentStreamEditor editor = new PdfContentStreamEditor(document, page) {
@Override
protected void write(ContentStreamWriter contentStreamWriter, Operator operator, List<COSBase> operands) throws IOException {
String operatorString = operator.getName();
if (TEXT_SHOWING_OPERATORS.contains(operatorString) && isInAreas()) {
if (currentlyReplacedColor == null)
{
PDColor currentFillColor = getGraphicsState().getNonStrokingColor();
if (!isBlack(currentFillColor))
{
currentlyReplacedColor = currentFillColor;
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, GRAY_BLACK_VALUES);
}
}
} else if (currentlyReplacedColor != null) {
PDColorSpace replacedColorSpace = currentlyReplacedColor.getColorSpace();
List<COSBase> replacedColorValues = new ArrayList<>();
for (float f : currentlyReplacedColor.getComponents())
replacedColorValues.add(new COSFloat(f));
if (replacedColorSpace instanceof PDDeviceCMYK)
super.write(contentStreamWriter, SET_NON_STROKING_CMYK, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceGray)
super.write(contentStreamWriter, SET_NON_STROKING_GRAY, replacedColorValues);
else if (replacedColorSpace instanceof PDDeviceRGB)
super.write(contentStreamWriter, SET_NON_STROKING_RGB, replacedColorValues);
else {
//TODO
}
currentlyReplacedColor = null;
}
super.write(contentStreamWriter, operator, operands);
before = null;
after = null;
}
PDColor currentlyReplacedColor = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
final Operator SET_NON_STROKING_CMYK = Operator.getOperator("k");
final Operator SET_NON_STROKING_RGB = Operator.getOperator("rg");
final Operator SET_NON_STROKING_GRAY = Operator.getOperator("g");
final List<COSBase> GRAY_BLACK_VALUES = Arrays.asList(COSInteger.ZERO);
@Override
protected void showText(byte[] string) throws IOException {
Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
if (before == null)
before = getTextMatrix().multiply(ctm);
super.showText(string);
after = getTextMatrix().multiply(ctm);
}
Matrix before = null;
Matrix after = null;
boolean isInAreas() {
return isInAreas(before) || isInAreas(after);
}
boolean isInAreas(Matrix m) {
return m != null && areas.stream().anyMatch(rect -> rect.contains(m.getTranslateX(), m.getTranslateY()));
}
};
editor.processPage(page);
}
document.save("WithoutSomeAnnotation-withBlackTextThere.pdf");