pdf - iText - 在不清理整行的情况下清理矩形中的文本

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

我们将不胜感激。

最佳答案

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

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

引用 OP 的第二组示例文件的更新答案

当前(最高 5.5.8)iText 清理代码确实存在一个问题:对于标记文件,此处使用的一些 PdfContentByte 方法在内容流中引入了额外的指令,这实际上损坏了它并重新定位了忽略损坏的 PDF 查看器眼中的一些文本。

更详细:

PdfCleanUpContentOperator.writeTextChunks 使用 canvas.setCharacterSpacing(0)canvas.setWordSpacing(0) 初始设置字符和单词间距0. 不幸的是,这些方法在标记文件的情况下检查正在构建的 Canvas 当前是否在文本对象中,并且(如果不是)启动文本对象。此检查取决于 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 重新测试,但在所有情况下我都得到了正确的结果。

因此,我无法重现您的问题。


我仔细查看了您的 output.pdf。它有点特殊,特别是它不包含由当前 iText 版本创建或操作的 PDF 的某些典型 block 。此外,内容流看起来非常不同。

因此,我假设在 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 中间;
  • 有效地将文本定位在 0, 0 如果 PDF 查看器忽略上述错误。

事实上,如果您查看 output.pdf 页面的底部,您会看到:

output.pdf, page bottom

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

如果没有这样的后处理器,您似乎没有正式发布的 iText 版本,而是完全不同的东西。

关于pdf - iText - 在不清理整行的情况下清理矩形中的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35374912/

相关文章:

javascript - 在 React 中显示二进制 PDF

excel - 仅将填充了单元格的电子表格保存为 PDF

java - 如何在itext pdf中添加columnText作为注释

android - 如何在 iText 中将文本的一部分设为粗体

c# - iTextSharp - 如何打开/读取/提取文件附件?

java - 使用 PDFBox 动态创建多页文档

ios - 在没有 baseURL 的 UIWebView 中加载 PDF

python - 将 ipython 输出自动化为 pdf

java - 使用证书对 pdf 进行 iText 加密会导致 bouncycaSTLe 错误

java - 使用 itext : UnitValue cannot be cast to BorderRadius 将 html 转换为 pdf 期间 iText 7 异常