pdfbox 写入压缩对象流

pdfbox writing compressed object streams

我正在合并多个文件,这些文件最初有 19mb。

但是结果一共是56mb。我怎样才能使这个最终值接近 19mb。 [编辑]

public void concatena(InputStream anterior, InputStream novo, OutputStream saida, List<String> marcadores)
    throws IOException {
    PDFMergerUtility pdfMerger = new PDFMergerUtility();
    pdfMerger.setDestinationStream(saida);
    PDDocument dest;
    PDDocument src;
    MemoryUsageSetting setupMainMemoryOnly = MemoryUsageSetting.setupMainMemoryOnly();
    if (anterior != null) {                     
        dest = PDDocument.load(anterior, setupMainMemoryOnly);
        src = PDDocument.load(novo, setupMainMemoryOnly);
    } else {
        dest = PDDocument.load(novo, setupMainMemoryOnly);
        src = new PDDocument();
    }       
    int totalPages = dest.getNumberOfPages();   
    pdfMerger.appendDocument(dest, src);
    criaMarcador(dest, totalPages, marcadores);
    saida = pdfMerger.getDestinationStream();
    dest.save(saida);
    dest.close();
    src.close();
}

抱歉,我还是不太会用Whosebug。我正在尝试 post 其余代码,但出现错误

[编辑 2 - 添加 criaMarcador 方法]

private void criaMarcador(PDDocument src, int numPaginas, List<String> marcadores) {
    if (marcadores != null && !marcadores.isEmpty()) {
        PDDocumentOutline documentOutline = src.getDocumentCatalog().getDocumentOutline();          
        if (documentOutline == null) {
            documentOutline = new PDDocumentOutline();
        }
        PDPage page;
        if (src.getNumberOfPages() == numPaginas) {
            page = src.getPage(0);
        } else {
            page = src.getPage(numPaginas);
        }
        PDOutlineItem bookmark = null;
        PDOutlineItem pai = null;
        String etiquetaAnterior = null;
        for (String etiqueta : marcadores) {                
            bookmark = bookmark(pai != null ? pai : documentOutline, etiqueta);
            if (bookmark == null) {
                if (etiquetaAnterior != null && !etiquetaAnterior.equals(etiqueta) && pai == null) {
                    pai = bookmark(documentOutline, etiquetaAnterior);
                }
                bookmark = new PDOutlineItem();
                bookmark.setTitle(etiqueta);
                if (marcadores.indexOf(etiqueta) == marcadores.size() - 1) {
                    bookmark.setDestination(page);
                }
                if (pai != null) {
                    pai.addLast(bookmark);
                    pai.openNode();
                } else {
                    documentOutline.addLast(bookmark);
                }
            } else {
                pai = bookmark;
            }
            etiquetaAnterior = etiqueta;
        }   
        src.getDocumentCatalog().setDocumentOutline(documentOutline);           
    }       
}

private PDOutlineItem bookmark(PDOutlineNode outline, String etiqueta) {             
    PDOutlineItem current = outline.getFirstChild();
    while (current != null) {
        if (current.getTitle().equals(etiqueta)) {
            return current;
        }
        bookmark(current, etiqueta);
        current = current.getNextSibling();
    }
    return current;
}

[编辑 3]这是用于测试的代码

public class PDFMergeTeste {


public static void main(String[] args) throws IOException {
    if (args.length == 1) {
        PDFMergeTeste teste = new PDFMergeTeste();
        teste.executa(args[0]);
    } else {
        System.err.println("Argumento tem que ser diretorio contendo arquivos .pdf com nomeclatura no padrão Autos");
    }
}

private void executa(String diretorioArquivos) throws IOException {
    File[] listFiles = new File(diretorioArquivos).listFiles((pathname) -> 
            pathname.getName().endsWith(".pdf") || pathname.getName().endsWith(".PDF"));
    List<File> lista = Arrays.asList(listFiles);
    lista.sort(Comparator.comparing(File::lastModified));
    PDFMerge merge = new PDFMerge();
    InputStream anterior = null;
    ByteArrayOutputStream saida = new ByteArrayOutputStream();
    for (File file : lista) {
        List<String> marcadores = marcadores(file.getName());           
        InputStream novo = new FileInputStream(file);           
        merge.concatena(anterior, novo, saida, marcadores);                     
        anterior = new ByteArrayInputStream(saida.toByteArray());
    }
    try (OutputStream pdf = new FileOutputStream(pathDestFile)) {
        saida.writeTo(pdf);
    }


}
private List<String> marcadores(String name) {
    String semExtensao = name.substring(0, name.indexOf(".pdf"));
    return Arrays.asList(semExtensao.split("_"));       
}

}

错误在executa方法中:

InputStream anterior = null;
ByteArrayOutputStream saida = new ByteArrayOutputStream();
for (File file : lista) {
    List<String> marcadores = marcadores(file.getName());           
    InputStream novo = new FileInputStream(file);           
    merge.concatena(anterior, novo, saida, marcadores);                     
    anterior = new ByteArrayInputStream(saida.toByteArray());
}

你的ByteArrayOutputStream saida在每个循环中都是re-used但是没有被清除in-between。因此,它包含

  • 处理文件 1 后:
    • 文件 1
  • 处理文件 2 后:
    • 文件 1
    • 文件 1 和文件 2 的串联
  • 处理文件 3 后:文件 1
    • 文件 1
    • 文件 1 和文件 2 的串联
    • 文件 1、文件 2 和文件 3 的串联
  • 处理文件 4 后:
    • 文件 1
    • 文件 1 和文件 2 的串联
    • 文件 1、文件 2 和文件 3 的串联
    • 文件 1 和文件 2 以及文件 3 和文件 4 的串联

(实际上,这仅适用于 PDFBox 试图变得更好并在后台修复损坏的输入文件,因为严格来说,这些文件串联已损坏,PDFBox 不需要能够解析它们。)

您可以通过在每次迭代开始时清除 saida 来解决此问题:

InputStream anterior = null;
ByteArrayOutputStream saida = new ByteArrayOutputStream();
for (File file : lista) {
    saida.reset();
    List<String> marcadores = marcadores(file.getName());           
    InputStream novo = new FileInputStream(file);           
    merge.concatena(anterior, novo, saida, marcadores);                     
    anterior = new ByteArrayInputStream(saida.toByteArray());
}

使用您的原始方法,您输入的结果大小将近 26 MB,使用固定方法大约为 5 MB,后者的大小大约等于输入文件大小的总和。