我正在尝试使用iText清理pdf文档中矩形内的文本。
以下是我正在使用的代码:
PdfReader pdfReader = null;
PdfStamper stamper = null;
try
{
int pageNo = 1;
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
pdfReader = new PdfReader("Test1.pdf");
stamper = new PdfStamper(pdfReader, new FileOutputStream("Test2.pdf"));
Rectangle linkLocation = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(pageNo, linkLocation, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
stamper.close();
}
catch (Exception e) {
e.printStackTrace();
}
pdfReader.close();
}
执行这段代码后,它会清除整行文本,而不是只清理给定矩形内的文本。
为了更好地解释事情,我附上了pdf文件。
在输入pdf中,我突出显示了文本,以显示我指定清理的矩形。
并且,在输出pdf中,您可以清楚地看到有灰色矩形,但如果您注意到它清理了整行文本。
任何帮助将不胜感激。
答案 0 :(得分:0)
OP最初提供的文件input.pdf
和output.pdf
不允许重现问题,但似乎根本不匹配。因此,有一个原始答案基本上表明该问题无法再现。
第二组文件Test1.pdf
和Test2.pdf
确实允许重现该问题,从而产生更新后的答案...
当前(最多5.5.8)iText清理代码确实存在问题:对于标记文件,此处使用的PdfContentByte
的一些方法会在内容流中引入额外的指令,实际上会损坏它并且在PDF观众眼中重新定位了一些忽略损坏的文本。
更详细:
PdfCleanUpContentOperator.writeTextChunks
使用canvas.setCharacterSpacing(0)
和canvas.setWordSpacing(0)
初步将字符和单词间距设置为0.不幸的是,在标记文件的情况下,这些方法会检查当前正在构建的画布是否在文本中object和(如果不是)启动文本对象。此检查取决于beginText
设置的本地标志;但在清理期间,文本对象不会使用该方法启动。因此,此处writeTextChunks
会插入额外的"BT 1 0 0 1 0 0 Tm"
序列,从而损坏流并重新定位以下文本。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
canvas.setCharacterSpacing(0);
canvas.setWordSpacing(0);
...
PdfCleanUpContentOperator.writeTextChunks
应该使用手工制作的Tc
和Tw
指令来触发此副作用。
private void writeTextChunks(Map<Integer, Float> structuredTJoperands, List<PdfCleanUpContentChunk> chunks, PdfContentByte canvas,
float characterSpacing, float wordSpacing, float fontSize, float horizontalScaling) throws IOException {
if (Float.compare(characterSpacing, 0.0f) != 0 && Float.compare(characterSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tc);
}
if (Float.compare(wordSpacing, 0.0f) != 0 && Float.compare(wordSpacing, -0.0f) != 0) {
new PdfNumber(0).toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(Tw);
}
canvas.getInternalBuffer().append((byte) '[');
通过此更改,OP的新示例文件“Test1.pdf”被示例代码正确编辑
@Test
public void testRedactJavishsTest1() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("Test1.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "Test1-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 202.3);
linkBounds.add(1, (float) 588.6);
linkBounds.add(2, (float) 265.8);
linkBounds.add(3, (float) 599.7);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
我只是尝试使用此测试方法重现您的问题
@Test
public void testRedactJavishsText() throws IOException, DocumentException
{
try ( InputStream resource = getClass().getResourceAsStream("input.pdf");
OutputStream result = new FileOutputStream(new File(OUTPUTDIR, "input-redactedJavish.pdf")) )
{
PdfReader reader = new PdfReader(resource);
PdfStamper stamper = new PdfStamper(reader, result);
List<Float> linkBounds = new ArrayList<Float>();
linkBounds.add(0, (float) 200.7);
linkBounds.add(1, (float) 547.3);
linkBounds.add(2, (float) 263.3);
linkBounds.add(3, (float) 558.4);
Rectangle linkLocation1 = new Rectangle(linkBounds.get(0), linkBounds.get(1), linkBounds.get(2), linkBounds.get(3));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(1, linkLocation1, BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations, stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
}
您的源PDF看起来像这样
结果是
而不是你的
我甚至使用您在评论中提到的iText版本5.5.5以及5.5.4进行了重新测试,但在所有情况下我都得到了正确的结果。
因此,我无法重现您的问题。
我仔细看了一下你的输出.pdf。它有点特殊,特别是它不包含当前iText版本创建或操作的PDF的典型块。此外,内容流看起来非常不同。
因此,我假设在iText对您的文件进行编辑后,其他一些工具经过后处理并且这样做会损坏它。
特别是准备插入编辑行的页面内容说明在input.pdf中如下所示:
q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
[...] TJ
在我直接从iText收到的版本中就像这样:
q
0.24 0 0 0.24 113.7055 548.04 cm
BT
0.0116 Tc
45 0 0 45 0 0 Tm
/TT5 1 Tf
0 Tc
0 Tw
[...] TJ
但是output.pdf中的相应行看起来像这样
BT
1 0 0 1 113.3 548.5 Tm
0 Tc
BT
1 0 0 1 0 0 Tm
0 Tc
[...] TJ
这里output.pdf中的说明是
BT ... ET
内部无效,可能没有其他文字对象,但您有两个BT
操作,彼此之间没有ET
; 事实上,如果你查看output.pdf页面的底部,你会看到:
因此,如果我假设有一些其他程序对iText结果进行后期处理是正确的,那么你应该修复后处理器。
如果没有这样的后处理器,你似乎没有官方发布的iText版本,但完全不同。