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

是否可以按位置使用PDFBox编辑PDF区域?

向杜吟
2023-03-14

背景

目前,我有一个解决方案,我在PDF中循环并在其中绘制黑色矩形。

所以我已经有了一个PDRectangle列表,它代表了我需要在pdf上填充/覆盖的正确区域,隐藏了我想要的所有文本。

问题所在

问题1:黑色矩形下方的文本很容易被复制、搜索或通过其他工具提取。

我通过展平我的pdf来解决这个问题(将其转换为图像,使其成为单层文档,黑色矩形不再被欺骗)。与此处描述的解决方案相同:使用pdfBox禁用pdf-text搜索

这不是真正的编辑,更像是一种变通方法。这让我想到

问题2:

我的最终PDF变成了一个图像文档,在那里我失去了所有的pdf属性,包括搜索、复制......而且这是一个慢得多的过程。我想保留所有的pdf属性,而编辑区域无论如何都无法读取。

我想要实现的目标

话虽如此,我想知道这是否可能,以及我如何才能进行实际的编辑,黑化矩形区域,因为我已经拥有了我需要的所有位置,使用PDFBox,保留pdf属性,不允许读取编辑区域。

注意:我知道PDFBox与旧的替换文本功能有问题,但在这里我有我需要的位置,以确保我准确地空白我需要的区域。

此外,我还接受了其他免费图书馆的建议。

技术规格:

PDFBox 2.0.21,Java 11.0.6 10,采用OpenJDK,MacOS Catalina 10.15.4,16gb,x86\U 64

我的代码

这是我绘制黑色矩形的方式:

private void draw(PDPage page, PDRectangle hitPdRectangle) throws IOException {

    PDPageContentStream content = new PDPageContentStream(pdDocument, page,
        PDPageContentStream.AppendMode.APPEND, false, false);
    content.setNonStrokingColor(0f);
    
    content.addRect(hitPdRectangle.getLowerLeftX(), 
        hitPdRectangle.getLowerLeftY()  -0.5f, 
        hitPdRectangle.getUpperRightX() - hitPdRectangle.getLowerLeftX(), 
        hitPdRectangle.getUpperRightY() - hitPdRectangle.getLowerLeftY());
    
    content.fill();
    content.close();
}

这就是我如何将其转换为图像PDF:

private PDDocument createNewRedactedPdf() throws IOException {
    PDFRenderer pdfRenderer = new PDFRenderer(pdDocument);

    PDDocument redactedDocument = new PDDocument();

    for (int pageIndex = 0; pageIndex < pdDocument.getNumberOfPages(); pageIndex++) {
        BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 200);

        String formatName = "jpg";
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, formatName, baos);

        byte[] bimg = baos.toByteArray();

        PDPage page = pdDocument.getPage(pageIndex);
        float pageWidth  = page.getMediaBox().getWidth();
        float pageHeight = page.getMediaBox().getHeight();

        PDPage pageDraw = new PDPage(new PDRectangle(pageWidth, pageHeight));
        redactedDocument.addPage(pageDraw);
        String imgSuffixName = pageIndex + "." + formatName;
        PDImageXObject img = PDImageXObject.createFromByteArray(redactedDocument, bimg,
            pdDocument.getDocument().getDocumentID() + imgSuffixName);

        try (PDPageContentStream contentStream
                 = new PDPageContentStream(redactedDocument, pageDraw, PDPageContentStream.AppendMode.OVERWRITE, false)) {

            contentStream.drawImage(img, 0, 0, pageWidth, pageHeight);
        }
    }

    return redactedDocument;
}

有什么想法吗?

共有1个答案

章哲彦
2023-03-14

你想要的是一个真正的编校功能,可以基于PDFBox实现,但它需要在其上进行大量编码(类似于在iText上实现的pdfSweep插件)。

特别是,你自己已经发现,仅仅在文本提取或复制的区域上绘制黑色矩形是不够的

因此,在代码中,您必须找到绘制文本的实际指令,以编辑并删除它们。但您不能简单地删除它们而不进行替换,否则您的修订可能会移动同一行上的其他文本。

但你不能简单地用相同数量的空格替换它们,或者按删除文本的宽度向右移动:考虑一下一个表格的情况,你想编辑一列,其中只有“是”和“否”条目。如果在编辑后,文本提取器返回三个空格,其中有一个“是”,两个空格,其中有一个“否”,任何查看这些结果的人都知道编辑区域里有什么。

您还必须清理实际文本绘制说明周围的说明。考虑再次使用“是”/“否”信息编辑列的示例,但这一次为了更加清楚,“是”以绿色绘制,“否”以红色绘制。如果只替换文本绘制说明,则具有同时提取颜色等属性的提取器的人将立即知道已编辑的信息。

在标记PDF的情况下,也必须检查标记属性。特别是有一个属性ActualText,它包含标记说明所代表的实际文本(特别是对于屏幕阅读器)。如果您只删除文本绘图说明,但保留标签及其属性,任何使用屏幕阅读器阅读的人甚至可能没有意识到您试图在他的屏幕阅读器向他阅读完整的原始文本时编辑某些内容。

因此,为了进行适当的修订,您必须解释所有当前的指令,确定它们所绘制的实际内容,并创建一组新的指令,这些指令可以绘制相同的内容,而不需要额外的指令,这些指令可能会泄露有关修订内容的某些信息。

在这里,我们只看了修改文本;在PDF页面上编辑矢量和位图图形也有类似数量的挑战需要克服,以便进行适当的编辑。

...

因此,实际编校所需的代码超出了堆栈溢出答案的范围。尽管如此,以上各项可能会帮助实现编校者避免落入编校代码过于幼稚的典型陷阱。

 类似资料:
  • 在我的程序中,有一个带有许多加速器的JMenu和一个可编辑的JTable。问题是,当编辑JTable时,加速器键仍然会被触发。 编辑上表中的任何单元格,有一个菜单项设置为哔哔声与加速器'n'。

  • 问题内容: 我正在阅读PDF并输出其中包含原始PDF的多个副本的PDF。我通过对PDFBox和iText做同样的事情来进行测试。如果我分别复制每个页面,iText会创建一个较小的输出。 问题: 在PDFBox中还有另一种方法可以使输出的PDF变小。 对于一个示例输入文件,使用两个工具生成两个副本到输出: 原始PDF大小:30K PDFBox(v 1.7.1)生成的PDF:84K iText(v 5

  • 我有一个JavaFX TableView,它有两列。此表使用观察列表作为其模型。 我想将其中一个设置为可编辑,另一个设置为不可编辑。 首先,我尝试了FXML方法: 但这不管用。表格始终不可编辑。 我还尝试了java方式: 但这也不管用。所有的桌子都不能修改。 如果TableColumn有一个可编辑的属性,那么它应该可以单独设置。 如果你知道我哪里做错了,请帮帮我。谢谢。 PS:measureNum

  • 我想自定义android studio编辑器背景,所以我想把图像设置为编辑器,有没有可能,有没有什么选项可以执行?

  • 我有几份产品数据表。每个文件都是一个单独的文件。我想做的是使用iText根据Web表单的答案生成一组摘要/建议的操作,然后将所有相关的数据表附加到这些操作中。这样,我只需要在浏览器中打开一个新选项卡来打印所有信息,而不是为摘要打开一个选项卡,为所需的每个数据表打开一个选项卡。 那么,使用iText可以做到这一点吗?

  • 是否可以编辑不能在C#中填写的PDF格式 > 现有的PDF包含图像、填充了数据和文本的表格。 已经尝试了itextsharp,但无法识别任何操作数据的AcroFields。