【问题标题】:How to remove Images from PDF File?如何从 PDF 文件中删除图像?
【发布时间】:2021-09-02 20:05:39
【问题描述】:

您好,感谢您回答我的问题。这个问题困扰了我很长时间。

这个QS我找了很久,在stack overFlow和google上看了很多文章,但是那些文章已经过时或者碎片化了,所以我不得不寻求帮助。 我希望有人可以帮助我,拜托。

public class TEST04 {
    public static void main(String[] args) throws IOException {
        System.out.println("Hi");
        //ori pdf file
        String oriPDFFile = IFileUtils.getDesktopPath().getAbsoluteFile() + "\\1.pdf";
        //out pdf file
        String outPDFFile = IFileUtils.getDesktopPath().getAbsoluteFile() + "\\2.pdf";
        strip(oriPDFFile, outPDFFile);
    }

    //parse
    public static void strip(String pdfFile, String pdfFileOut) throws IOException {
        //load ori pdf file
        PDDocument document = PDDocument.load(new File(pdfFile));
        //get All pages
        List<PDPage> pageList = IterUtil.toList(document.getDocumentCatalog().getPages());

        for (int i = 0; i < pageList.size(); i++) {
            PDPage page = pageList.get(i);
            COSDictionary newDictionary = new COSDictionary(page.getCOSObject());
            PDFStreamParser parser = new PDFStreamParser(page);
            List tokens = parser.getTokens();
            List newTokens = new ArrayList();

            for (int j = 0; j < tokens.size(); j++) {
                Object token = tokens.get(j);
                if (token instanceof Operator) {
                    Operator operator = (Operator) token;
                    if (operator.getName().equals("Do")) {
                        COSName cosName = (COSName) newTokens.remove(newTokens.size() - 1);
                        deleteObject(newDictionary, cosName);
                        continue;
                    }
                }
                newTokens.add(token);
            }
            PDStream newContents = new PDStream(document);
            try (OutputStream outputStream = newContents.createOutputStream()) {
                ContentStreamWriter writer = new ContentStreamWriter(outputStream);
                writer.writeTokens(newTokens);
            }
            page.setContents(newContents);

//            ContentStreamWriter writer = new ContentStreamWriter(newContents.createOutputStream());
//            writer.writeTokens( newTokens );
//            page.setContents(newContents);
            
            PDResources newResources = new PDResources(newDictionary);
            page.setResources(newResources);

        }
        document.save(pdfFileOut);
        document.close();
    }
    //delete
    public static boolean deleteObject(COSDictionary d, COSName name) {
        for(COSName key : d.keySet()) {
            if( name.equals(key) ) {
                d.removeItem(key);
                return true;
            }
            COSBase object = d.getDictionaryObject(key);
            if(object instanceof COSDictionary) {
                if( deleteObject((COSDictionary)object, name) ) {
                    return true;
                }
            }
        }
        return false;
    }
}

堆栈跟踪:

【问题讨论】:

    标签: java pdf pdfbox


    【解决方案1】:

    它的工作方式与示例 RemoveAllText.java 中的相同,只是标签不同。

    使用此示例中的代码,只需使用“Do”而不是“Tj”。

    当然,如果你需要加载元数据等,你应该枚举并检查图片投掷的页面资源(如我的示例)

    【讨论】:

    • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
    • 可以给我详细代码吗?非常感谢
    • RemoveAllText.java 在源代码下载中。
    • 我喜欢这个anwser,但是我复制并更改了代码,它不起作用......
    【解决方案2】:

    按照Ali Yavari's answer 中的提示,您创建了一个测试类。不幸的是,测试代码产生了异常。这个答案的重点是修复你的代码。

    根据您发布的堆栈跟踪,保存文档时发生异常的图像;某些流被要求提供InputStream,但它失败并显示消息“当存在打开的流写入器时无法读取”。

    那么,让我们看看您的代码在哪里打开了流编写器,但没有再次关闭它:

    PDStream newContents = new PDStream(document);
    ContentStreamWriter writer = new ContentStreamWriter(newContents.createOutputStream());
    writer.writeTokens( newTokens );
    page.setContents(newContents);
    

    确实,您在这里向流 (PDStream newContents) 请求写入 (newContents.createOutputStream()) 的内容,但不要关闭它。

    你可以这样做:

    PDStream newContents = new PDStream(document);
    try (OutputStream outputStream = newContents.createOutputStream()) {
        ContentStreamWriter writer = new ContentStreamWriter(outputStream);
        writer.writeTokens(newTokens);
    }
    page.setContents(newContents);
    

    附注,您将不得不重写您对 newDictionary 对象所做的操作。目前你

    1. 使用页面字典条目对其进行初始化,
    2. 递归地删除所有条目,其键是您删除的图像的名称,并且
    3. 将页面资源设置为此字典。

    第 2 项可以删除的内容比您实际想要的要多得多,不同字典中的相同名称可能指的是具有完全不同含义的条目。此外,您无需进一步检查即可递归;如果字典之间存在循环关系,这可能会导致无限递归,即堆栈溢出异常。

    第 3 项不恰当地将这个被操纵的页面克隆设置为原始页面的资源。这会创建一个完全损坏的页面结构。

    相反,您应该从页面 (resources = page.getResources()) 中检索资源,然后将图像移至 null (resources.put(cosName, (PDXObject)null)) 以将其删除。

    【讨论】:

    • 嘿mkl,非常感谢,我已经改了代码,没有错误,但是outPDFFile'页面是空的。ori pdf文件是3页,out pdf文件也是3页,但是每一页都是空的。所以我不知道怎么了,你能给我一个完整的代码来删除图像表单页面吗?非常感谢。
    • “我更改了我的代码” - 你是如何更改的?您是否也考虑过我在水平规则下写的内容?您是如何解决对资源的操纵的?
    • 你好,谢谢你的帮助。我已经通过你的 eda 更新了我的问题代码,它没有错误,但也是空的,所以你能给我一些代码......来修复它。
    • @Colin “我已经通过你的 eda 更新了我的问题代码,它没有错误,但也是空的,所以你能给我一些代码......来修复它。” - 您是否阅读了我在水平线下方的答案中的旁注?该附注解释了为什么您的代码会创建一个完全损坏的页面结构以及如何更改它...
    • 我看到了这个:" 你应该从页面中检索资源 (resources = page.getResources()) 并将图像设置为 null (resources.put(cosName,(PDXObject)null )).",但是你知道,我看不懂你的笔记,因为ori类是从别人那里抄来的,我不太了解PDFBox,所以求你给我一个完整的代码。事实上,我只需要删除指定的图像,它们有一个 cosName,就像 img7,img8 一样,所以,你能告诉我如何修复我的代码
    【解决方案3】:

    my other answer 中,我专注于建议如何修复问题中的代码。在这里,我将重点介绍一种不同的任务方法。

    在您的代码中,您尝试通过检查页面内容流、在其中找到绘制 XObject 的 Do 操作并删除此指令和引用的 XObject 来删除位图图像。

    将资源中的所有图像 XObject 替换为空表单 XObject 会更容易替换。这是这里使用的方法。

    由于这种方法很容易实现,我将其扩展为不仅遍历页面的直接资源,还迭代到嵌入式表单 XObjects 和模式。

    void replaceBitmapImagesResources(PDDocument document) throws IOException {
        PDFormXObject pdFormXObject = new PDFormXObject(document);
        pdFormXObject.setBBox(new PDRectangle(1, 1));
        for (PDPage pdPage : document.getPages()) {
            replaceBitmapImagesResources(pdPage.getResources(), pdFormXObject);
        }
    }
    
    void replaceBitmapImagesResources(PDResources resources, PDFormXObject formXObject) throws IOException {
        if (resources == null)
            return;
    
        for (COSName cosName : resources.getPatternNames()) {
            PDAbstractPattern pdAbstractPattern = resources.getPattern(cosName);
            if (pdAbstractPattern instanceof PDTilingPattern) {
                PDTilingPattern pdTilingPattern = (PDTilingPattern) pdAbstractPattern;
                replaceBitmapImagesResources(pdTilingPattern.getResources(), formXObject);
            }
        }
    
        List<COSName> xobjectsToReplace = new ArrayList<>();
        for (COSName cosName : resources.getXObjectNames()) {
            PDXObject pdxObject = resources.getXObject(cosName);
            if (pdxObject instanceof PDImageXObject) {
                xobjectsToReplace.add(cosName);
            } else if (pdxObject instanceof PDFormXObject) {
                PDFormXObject pdFormXObject = (PDFormXObject) pdxObject;
                replaceBitmapImagesResources(pdFormXObject.getResources(), formXObject);
            }
        }
    
        for (COSName cosName : xobjectsToReplace) {
            resources.put(cosName, formXObject);
        }
    }
    

    (RemoveImages 辅助方法)

    要将此方法应用于PDDocument,只需调用第一个replaceBitmapImagesResources,并将该文档作为参数。

    注意:我尽量保持代码简单;对于生产用途,请记住在此处限制递归,以防止在某些 PDF 中 XObject 或模式直接或间接地调用自身。此外,您可能需要检查页面注释和模板页面的资源。

    【讨论】:

      猜你喜欢
      • 2011-09-21
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 2022-01-22
      • 1970-01-01
      • 2011-10-13
      • 1970-01-01
      相关资源
      最近更新 更多