使用iTextPDF修剪页面的空白

我有一个pdf,其中包含一些数据,其次是一些空白。 我不知道数据有多大,但是我希望在数据之后删除空白

PdfReader reader = new PdfReader(PDFLOCATION); Rectangle rect = new Rectangle(700, 2000); Document document = new Document(rect); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(SAVELCATION)); document.open(); int n = reader.getNumberOfPages(); PdfImportedPage page; for (int i = 1; i <= n; i++) { document.newPage(); page = writer.getImportedPage(reader, i); Image instance = Image.getInstance(page); document.add(instance); } document.close(); 

有没有办法在新文档中剪切/修剪每个页面的空白? 此PDF包含vectorgraphics。

我使用iTextPDF,但可以切换到任何Java库(mavenized,首选Apache许可证)

由于没有实际的解决scheme已发布,在这里附带的itext-questions邮件列表线程的一些指针:

  1. 由于您只想修剪页面,所以这不是PdfWriter + getImportedPage使用情况,而是使用了PdfStamper 。 您使用PdfStamper主代码可能如下所示:

     PdfReader reader = new PdfReader(resourceStream); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("target/test-outputs/test-trimmed-stamper.pdf")); // Go through all pages int n = reader.getNumberOfPages(); for (int i = 1; i <= n; i++) { Rectangle pageSize = reader.getPageSize(i); Rectangle rect = getOutputPageSize(pageSize, reader, i); PdfDictionary page = reader.getPageN(i); page.put(PdfName.CROPBOX, new PdfArray(new float[]{rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()})); stamper.markUsed(page); } stamper.close(); 

    正如你所看到的,我还为你的getOutputPageSize方法添加了另一个参数。 这是页码。 毕竟,在不同的页面上裁剪的空白量可能会有所不同。

  2. 如果源文档不包含vectorgraphics,则可以简单地使用iTextparsing器包类。 甚至已经有一个基于它们的TextMarginFinder 。 在这种情况下, getOutputPageSize方法(带有附加的页面参数)可能如下所示:

     private Rectangle getOutputPageSize(Rectangle pageSize, PdfReader reader, int page) throws IOException { PdfReaderContentParser parser = new PdfReaderContentParser(reader); TextMarginFinder finder = parser.processContent(page, new TextMarginFinder()); Rectangle result = new Rectangle(finder.getLlx(), finder.getLly(), finder.getUrx(), finder.getUry()); System.out.printf("Text/bitmap boundary: %f,%f to %f, %f\n", finder.getLlx(), finder.getLly(), finder.getUrx(), finder.getUry()); return result; } 

    使用这个方法与你的文件test.pdf结果如下:

    在这里输入图像描述

    正如你看到的代码修剪根据文本 (和位图图像)的内容在页面上。

  3. 为了find与vectorgraphics相关的边界框,你基本上也必须这样做,但是你必须扩展这里使用的parsing器框架来通知它的监听器( TextMarginFinder基本上是从parsing器框架发送的绘制事件的监听器)graphics操作也是如此。 这是不平凡的,特别是如果你不知道PDF语法。

  4. 如果您的PDF修剪不是过于通用,但可以强制在相关位置包含一些文本或位图graphics,但是,您可以使用上面的示例代码(可能会进行较小的更改)。

    例如,如果您的PDF始终以文本开头,底部文本结束,则可以更改getOutputPageSize以创build如下所示的结果矩形:

     Rectangle result = new Rectangle(pageSize.getLeft(), finder.getLly(), pageSize.getRight(), finder.getUry()); 

    这只会修剪顶部和底部的空白空间:

    在这里输入图像描述

    根据您的input数据池和要求,这可能就足够了。

    或者,您可以根据您对input数据的了解使用其他启发式。 如果您对文本的位置有所了解(例如标题始终居中,而其他文本总是从左侧开始),则可以轻松扩展TextMarginFinder以利用此知识。


最近(2015年4月,iText 5.5.6-SNAPSHOT)的改进

当前的开发版本5.5.6-SNAPSHOT扩展了parsing器包,还包括vector图parsing。 这允许扩展iText的原始TextMarginFinder类,实现新的ExtRenderListener方法,如下所示:

 @Override public void modifyPath(PathConstructionRenderInfo renderInfo) { List<Vector> points = new ArrayList<Vector>(); if (renderInfo.getOperation() == PathConstructionRenderInfo.RECT) { float x = renderInfo.getSegmentData().get(0); float y = renderInfo.getSegmentData().get(1); float w = renderInfo.getSegmentData().get(2); float h = renderInfo.getSegmentData().get(3); points.add(new Vector(x, y, 1)); points.add(new Vector(x+w, y, 1)); points.add(new Vector(x, y+h, 1)); points.add(new Vector(x+w, y+h, 1)); } else if (renderInfo.getSegmentData() != null) { for (int i = 0; i < renderInfo.getSegmentData().size()-1; i+=2) { points.add(new Vector(renderInfo.getSegmentData().get(i), renderInfo.getSegmentData().get(i+1), 1)); } } for (Vector point: points) { point = point.cross(renderInfo.getCtm()); Rectangle2D.Float pointRectangle = new Rectangle2D.Float(point.get(Vector.I1), point.get(Vector.I2), 0, 0); if (currentPathRectangle == null) currentPathRectangle = pointRectangle; else currentPathRectangle.add(pointRectangle); } } @Override public Path renderPath(PathPaintingRenderInfo renderInfo) { if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP) { if (textRectangle == null) textRectangle = currentPathRectangle; else textRectangle.add(currentPathRectangle); } currentPathRectangle = null; return null; } @Override public void clipPath(int rule) { } 

(完整来源: MarginFinder.java )

使用这个类来修剪空白的结果

在这里输入图像描述

这几乎是人们所希望的。

当心:上面的实现远不是最优的。 它甚至不正确,因为它包含了太多的所有曲线控制点。 此外,它忽略像线宽或楔形types的东西。 它实际上仅仅是一个概念certificate。

所有的testing代码都在TestTrimPdfPage.java中 。