PDFBox 2.0 读取单页并 write/save 到新文件

PDFBox 2.0 Read single page and write/save to a new File

基于 this SO question 我尝试通读 pdf 文件中的每一页。这样做的背景是,我试图用完全空白的页面替换不包含任何文本内容但包含图像的页面。这样做的背景是 pdf 可以包含可能包含图像的空白页。这些页面确实需要存在,因为它们即将用双面打印。

但是对于 PDFBox 2.0,这似乎有点复杂,因为每次我试图保存新生成的 PDDocument 时,我都会 运行 进入堆栈跟踪。 PDFBox 2.0 的新版本是否应该这样做?我是否应该避免关闭 PDDocument buffer,因为将其排除在外,示例程序会毫无例外地执行 运行,这可能会产生什么潜在的副作用?

可以在此处查看一个简单的 运行ning 示例。您可以使用任何 pdf 文件,因为结果将是一个具有相同页数的 pdf 文件,应该为空:

public static void main(String[] args) throws IOException {
    // Load a simple pdf file
    PDDocument d = PDDocument.load(new File("D:\test.pdf"));
    // This should be our new output pdf
    PDDocument c = new PDDocument();
    for(int i = 0;i<d.getNumberOfPages();++i) {
        // From the SO question, create a new PDDocument and just add the single page
        PDDocument buffer = new PDDocument();
        PDPage page = d.getPage(i);
        buffer.addPage(page);

        // Here i´d check if it has content but gonna leave it out now

        // Reassign the page variable to generate a "blank" pdf
        page = new PDPage(); 

        // In order to let some printers not ignore the blank page I have to 
        // write white text on the white background.
        PDPageContentStream contentStream = new PDPageContentStream(buffer, page);

        PDFont font = PDType1Font.HELVETICA_BOLD;
        contentStream.beginText();
        contentStream.setNonStrokingColor(Color.white); // !!!!!!
        contentStream.setFont( font, 6 );
        contentStream.newLineAtOffset(100, 700);
        contentStream.showText("Empty page");
        contentStream.endText();
        contentStream.close();
        // Close the buffer document, if i comment it out the exception is gone
        buffer.close();
        // Add the blank page
        c.addPage(page);
    }
    d.close();
    // The exception occurs here and seems to be connected with the closing of the buffer document
    c.save("D:\newtest.pdf");
    c.close();
}

堆栈跟踪:

Exception in thread "main" java.io.IOException: Scratch file already closed
at org.apache.pdfbox.io.ScratchFile.checkClosed(ScratchFile.java:390)
at org.apache.pdfbox.io.ScratchFileBuffer.checkClosed(ScratchFileBuffer.java:99)
at org.apache.pdfbox.io.ScratchFileBuffer.seek(ScratchFileBuffer.java:295)
at org.apache.pdfbox.io.RandomAccessInputStream.restorePosition(RandomAccessInputStream.java:47)
at org.apache.pdfbox.io.RandomAccessInputStream.read(RandomAccessInputStream.java:78)
at java.io.InputStream.read(InputStream.java:101)
at org.apache.pdfbox.io.IOUtils.copy(IOUtils.java:66)
at org.apache.pdfbox.pdfwriter.COSWriter.visitFromStream(COSWriter.java:1134)
at org.apache.pdfbox.cos.COSStream.accept(COSStream.java:372)
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteObject(COSWriter.java:533)
at org.apache.pdfbox.pdfwriter.COSWriter.doWriteBody(COSWriter.java:450)
at org.apache.pdfbox.pdfwriter.COSWriter.visitFromDocument(COSWriter.java:1034)
at org.apache.pdfbox.cos.COSDocument.accept(COSDocument.java:409)
at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1284)
at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1185)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1110)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1082)
at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1070)
at pdftools.Test.main(Test.java:41)

您的代码有些混乱,但问题的核心在于,在 2.0 中,如果您正在另一个文档中使用它们的页面,则不应关闭文档。

所以这里有一些解决方案:

  • 不要关闭缓冲区文档,而是保留这些文档直到完成
  • 两次创建页面及其内容
  • 仅为目的地创建新页面(为什么要为 "buffer" 创建它,而您无论如何都在转储?)
  • 不要使用 addPage() 复制页面,而是使用 importPage()。这将进行深拷贝。

我一直在寻找 2021 年的解决方案,现在可以这样做 (Java/Kotlin)。示例从 PDF 文件中提取页面并将其保存在额外文件中。

import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.pdmodel.PDPage
import java.io.File

val inputPdf = File("C:\myInput.pdf")
val outputPdf = File("C:\myOutput.pdf")

fun main(args: Array<String>) =
    PDDocument.load(inputPdf).use {
        savePageToExtraFile(it.pages.first(), outputPdf)
    }

fun savePageToExtraFile(page: PDPage, outputFile: File) =
    PDDocument().use {
        it.importPage(page)
        it.save(outputFile)
    }