复制时页面在 PDFBox 的新文档中被裁剪
Page is cropped in new document in PDFBox while copying
我正在尝试将单个 PDF 拆分为多个。比如10页文档变成10页单页文档。
PDDocument source = PDDocument.load(input_file);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.addPage(page);
output.save(file);
output.close();
这里的问题是,新文档的页面大小与原始文档不同。所以一些文本在新文档中被裁剪或丢失。我正在使用 PDFBox 2.0,如何避免这种情况?
更新:
谢谢@mkl.
Splitter 施展了魔法。这是更新后的工作部分,
public static void extractAndCreateDocument(SplitMeta meta, PDDocument source)
throws IOException {
File file = new File(meta.getFilename());
Splitter splitter = new Splitter();
splitter.setStartPage(meta.getStart());
splitter.setEndPage(meta.getEnd());
splitter.setSplitAtPage(meta.getEnd());
List<PDDocument> docs = splitter.split(source);
if(docs.size() > 0){
PDDocument output = docs.get(0);
output.save(file);
output.close();
}
}
public class SplitMeta {
private String filename;
private int start;
private int end;
public SplitMeta() {
}
}
不幸的是,OP 没有提供示例文档来重现该问题。因此,我不得不猜测。
我假设问题出在没有立即链接到页面对象而是从其父对象继承的对象上。
在那种情况下使用 PDDocument.addPage
是错误的选择,因为此方法仅将给定的页面对象添加到目标文档页面树而不考虑继承的内容。
相反,应该使用 PDDocument.importPage
,记录为:
/**
* This will import and copy the contents from another location. Currently the content stream is stored in a scratch
* file. The scratch file is associated with the document. If you are adding a page to this document from another
* document and want to copy the contents to this document's scratch file then use this method otherwise just use
* the {@link #addPage} method.
*
* Unlike {@link #addPage}, this method does a deep copy. If your page has annotations, and if
* these link to pages not in the target document, then the target document might become huge.
* What you need to do is to delete page references of such annotations. See
* <a href="">here</a> for how to do this.
*
* @param page The page to import.
* @return The page that was imported.
*
* @throws IOException If there is an error copying the page.
*/
public PDPage importPage(PDPage page) throws IOException
实际上,即使是这种方法也可能不够用,因为它没有考虑所有继承的属性,但是查看 Splitter
实用程序 class 可以得出必须做的事情的印象:
PDPage imported = getDestinationDocument().importPage(page);
imported.setCropBox(page.getCropBox());
imported.setMediaBox(page.getMediaBox());
// only the resources of the page will be copied
imported.setResources(page.getResources());
imported.setRotation(page.getRotation());
// remove page links to avoid copying not needed resources
processAnnotations(imported);
利用辅助方法
private void processAnnotations(PDPage imported) throws IOException
{
List<PDAnnotation> annotations = imported.getAnnotations();
for (PDAnnotation annotation : annotations)
{
if (annotation instanceof PDAnnotationLink)
{
PDAnnotationLink link = (PDAnnotationLink)annotation;
PDDestination destination = link.getDestination();
if (destination == null && link.getAction() != null)
{
PDAction action = link.getAction();
if (action instanceof PDActionGoTo)
{
destination = ((PDActionGoTo)action).getDestination();
}
}
if (destination instanceof PDPageDestination)
{
// TODO preserve links to pages within the splitted result
((PDPageDestination) destination).setPage(null);
}
}
// TODO preserve links to pages within the splitted result
annotation.setPage(null);
}
}
当您尝试将单个 PDF 拆分为多个,例如将 10 页文档拆分为 10 个单页文档,您可能需要使用此 Splitter
实用程序 class 原样。
测试
为了测试这些方法,我使用了 PDF Clown 示例输出 AnnotationSample.Standard.pdf 的输出,因为该库在很大程度上依赖于页面树值的继承。因此,我使用 PDDocument.addPage
、PDDocument.importPage
或 Splitter
将其唯一页面的内容复制到一个新文档中,如下所示:
PDDocument source = PDDocument.load(resource);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.addPage(page);
output.save(new File(RESULT_FOLDER, "PageAddedFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithAddPage
)
PDDocument source = PDDocument.load(resource);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.importPage(page);
output.save(new File(RESULT_FOLDER, "PageImportedFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithImportPage
)
PDDocument source = PDDocument.load(resource);
Splitter splitter = new Splitter();
List<PDDocument> results = splitter.split(source);
Assert.assertEquals("Expected exactly one result document from splitting a single page document.", 1, results.size());
PDDocument output = results.get(0);
output.save(new File(RESULT_FOLDER, "PageSplitFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithSplitter
)
只有最终测试忠实地复制了页面。
我正在尝试将单个 PDF 拆分为多个。比如10页文档变成10页单页文档。
PDDocument source = PDDocument.load(input_file);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.addPage(page);
output.save(file);
output.close();
这里的问题是,新文档的页面大小与原始文档不同。所以一些文本在新文档中被裁剪或丢失。我正在使用 PDFBox 2.0,如何避免这种情况?
更新: 谢谢@mkl.
Splitter 施展了魔法。这是更新后的工作部分,
public static void extractAndCreateDocument(SplitMeta meta, PDDocument source)
throws IOException {
File file = new File(meta.getFilename());
Splitter splitter = new Splitter();
splitter.setStartPage(meta.getStart());
splitter.setEndPage(meta.getEnd());
splitter.setSplitAtPage(meta.getEnd());
List<PDDocument> docs = splitter.split(source);
if(docs.size() > 0){
PDDocument output = docs.get(0);
output.save(file);
output.close();
}
}
public class SplitMeta {
private String filename;
private int start;
private int end;
public SplitMeta() {
}
}
不幸的是,OP 没有提供示例文档来重现该问题。因此,我不得不猜测。
我假设问题出在没有立即链接到页面对象而是从其父对象继承的对象上。
在那种情况下使用 PDDocument.addPage
是错误的选择,因为此方法仅将给定的页面对象添加到目标文档页面树而不考虑继承的内容。
相反,应该使用 PDDocument.importPage
,记录为:
/**
* This will import and copy the contents from another location. Currently the content stream is stored in a scratch
* file. The scratch file is associated with the document. If you are adding a page to this document from another
* document and want to copy the contents to this document's scratch file then use this method otherwise just use
* the {@link #addPage} method.
*
* Unlike {@link #addPage}, this method does a deep copy. If your page has annotations, and if
* these link to pages not in the target document, then the target document might become huge.
* What you need to do is to delete page references of such annotations. See
* <a href="">here</a> for how to do this.
*
* @param page The page to import.
* @return The page that was imported.
*
* @throws IOException If there is an error copying the page.
*/
public PDPage importPage(PDPage page) throws IOException
实际上,即使是这种方法也可能不够用,因为它没有考虑所有继承的属性,但是查看 Splitter
实用程序 class 可以得出必须做的事情的印象:
PDPage imported = getDestinationDocument().importPage(page);
imported.setCropBox(page.getCropBox());
imported.setMediaBox(page.getMediaBox());
// only the resources of the page will be copied
imported.setResources(page.getResources());
imported.setRotation(page.getRotation());
// remove page links to avoid copying not needed resources
processAnnotations(imported);
利用辅助方法
private void processAnnotations(PDPage imported) throws IOException
{
List<PDAnnotation> annotations = imported.getAnnotations();
for (PDAnnotation annotation : annotations)
{
if (annotation instanceof PDAnnotationLink)
{
PDAnnotationLink link = (PDAnnotationLink)annotation;
PDDestination destination = link.getDestination();
if (destination == null && link.getAction() != null)
{
PDAction action = link.getAction();
if (action instanceof PDActionGoTo)
{
destination = ((PDActionGoTo)action).getDestination();
}
}
if (destination instanceof PDPageDestination)
{
// TODO preserve links to pages within the splitted result
((PDPageDestination) destination).setPage(null);
}
}
// TODO preserve links to pages within the splitted result
annotation.setPage(null);
}
}
当您尝试将单个 PDF 拆分为多个,例如将 10 页文档拆分为 10 个单页文档,您可能需要使用此 Splitter
实用程序 class 原样。
测试
为了测试这些方法,我使用了 PDF Clown 示例输出 AnnotationSample.Standard.pdf 的输出,因为该库在很大程度上依赖于页面树值的继承。因此,我使用 PDDocument.addPage
、PDDocument.importPage
或 Splitter
将其唯一页面的内容复制到一个新文档中,如下所示:
PDDocument source = PDDocument.load(resource);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.addPage(page);
output.save(new File(RESULT_FOLDER, "PageAddedFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithAddPage
)
PDDocument source = PDDocument.load(resource);
PDDocument output = new PDDocument();
PDPage page = source.getPages().get(0);
output.importPage(page);
output.save(new File(RESULT_FOLDER, "PageImportedFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithImportPage
)
PDDocument source = PDDocument.load(resource);
Splitter splitter = new Splitter();
List<PDDocument> results = splitter.split(source);
Assert.assertEquals("Expected exactly one result document from splitting a single page document.", 1, results.size());
PDDocument output = results.get(0);
output.save(new File(RESULT_FOLDER, "PageSplitFromAnnotationSample.Standard.pdf"));
output.close();
(CopyPages.java 测试 testWithSplitter
)
只有最终测试忠实地复制了页面。