当前位置: 首页 > 知识库问答 >
问题:

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

龙成仁
2023-03-14

下面是我正在使用的代码:

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文档。

任何帮助都将不胜感激。

共有1个答案

樊令秋
2023-03-14

最初提供的OP文件input.pdfoutput.pdf不允许重现该问题,而是看起来根本不匹配。因此,有一个最初的答案基本上表明,这个问题不能复制。

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

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

更详细地说:

pdfcleanupcontentoperator.writetextchunks使用canvas.setcharacterspacing(0)canvas.setwordspacing(0)最初将字符和单词间距设置为0。不幸的是,这些方法在标记文件的情况下,检查正在构建的画布当前是否在文本对象中,并(如果不是)启动文本对象。此检查取决于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)

我只是想用这个测试方法重现你的问题

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

我甚至使用评论中提到的iText版本5.5.5和5.5.4重新测试,但在所有情况下,我都得到了正确的结果。

因此,我不能重复你的问题。

我仔细看了你的输出。它有点奇怪,特别是它不包含由当前iText版本创建或操作的PDFs典型的某些块。此外,内容流看起来非常不同。

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
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
BT
1 0 0 1 113.3 548.5 Tm
0 Tc
BT
1 0 0 1 0 0 Tm
0 Tc 
[...] TJ

以下是输出。pdf中的说明

  • 无效,因为在文本对象中bt...et可能没有其他文本对象,但您有两个bt操作相互跟随,而中间没有et
  • 如果PDF查看器忽略上面提到的错误,则有效地将文本定位在0,0。

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

 类似资料:
  • 问题内容: 我正在研究模仿Paint的程序。问题是当我绘制新形状时,以前的形状会被删除。我试图注释掉我的paintComponents的超级调用,该调用可以工作,但是留下了太多的绘图。 问题答案: 正如您所发现的,您需要调用,否则背景不会被绘制,并且一切都是一团糟。问题在于,由于该字段只能是单个值,因此一次只能绘制一个形状。一种解决方案是创建一个形状,然后在in中绘制每个形状。

  • 问题内容: 我进行了一些测试,检查当某些字段中的文本无效时是否出现正确的错误消息。有效性检查之一是某个textarea元素不为空。 如果此文本区域中已经有文本,我如何告诉selenium清除该字段? 就像是: 问题答案:

  • 问题内容: 我进行了一些测试,当某些字段中的文本无效时,我会检查是否出现正确的错误消息。一种有效性检查是某个textarea元素不为空。 如果该文本区域中已经有文本,我如何告诉硒清除该字段? 就像是: 问题答案:

  • 我有一个如下定义的表视图 当用户单击时,我使用单击的项目详细信息重新加载表。在我重新加载之前,我对表项调用清除 现在,当我单击另一个项目并尝试重新加载表格时,它看起来像下面这样。这是非常奇怪的,因为应该只有一行,但行的大小是一样的,就像从以前的点击,其余的行是空的,除了图标。当我点击图标时,没有任何反应。任何人以前都面临过这种情况。有人知道这是什么原因吗? 我的工具定义如下。它们只是2 的

  • 我正在使用Monkeyrunner(automation)在Android设备(三星S2)上测试一个应用程序,因为它不允许我看到屏幕上的图像模式,所以我必须依靠adb logcat(使用Windows)来查找特定消息,以便知道我的自动化脚本的下一页何时加载。 为了在logcat中查找特定(唯一)消息,对于我的monkeyrun脚本,我必须清除所有adb日志,然后执行搜索。 有没有一种方法可以只清除

  • 问题内容: 我遇到了一个问题。我不知道如何在不将后记留空的情况下删除行。 我正在使用Apache-POI 3.9,使用下一个代码时出现错误: 更新版本: 我找到了解决方案:但是由于每个删除行都减少了lastRowNum,所以我得到了空指针。 这是新版本: 最后更新: ManishChristian帮助我解决了这个问题! 问题答案: 试试下面的代码,它应该可以工作: 每次删除一行都需要减少一。并再次