iText - 清理矩形文本而不清除整行

时间:2016-02-13 01:16:15

标签: pdf itext

我正在尝试使用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中,您可以清楚地看到有灰色矩形,但如果您注意到它清理了整行文本。

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

OP最初提供的文件input.pdfoutput.pdf不允许重现问题,但似乎根本不匹配。因此,有一个原始答案基本上表明该问题无法再现。

第二组文件Test1.pdfTest2.pdf确实允许重现该问题,从而产生更新后的答案...

参考OP的第二组样本文件

更新了答案

当前(最多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应该使用手工制作的TcTw指令来触发此副作用。

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

RedactText.java

原始答案涉及OP的原始样本文件

我只是尝试使用此测试方法重现您的问题

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

RedactText.java

您的源PDF看起来像这样

input.pdf

结果是

input-redactedJavish.pdf

而不是你的

output.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;
  • 如果PDF查看器忽略上述错误,则有效地将文本定位在0,0。

事实上,如果你查看o​​utput.pdf页面的底部,你会看到:

output.pdf, page bottom

因此,如果我假设有一些其他程序对iText结果进行后期处理是正确的,那么你应该修复后处理器。

如果没有这样的后处理器,你似乎没有官方发布的iText版本,但完全不同。