首页 > 解决方案 > 如何从 PDF 文件中删除图像?

问题描述

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

这个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;
    }
}

堆栈跟踪: 错误日志

标签: javapdfpdfbox

解决方案


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

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

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

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

实际上,在这里您要求流 (the 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)) 来删除它们。


推荐阅读