由于没有发布实际的解决方案,这里有一些来自随附的itext-questions 邮件列表线程的指针:
由于您只想修剪页面,因此这不是PdfWriter
+getImportedPage
使用的情况,而是使用的PdfStamper
情况。您使用 a 的主要代码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
方法添加了另一个参数。它是页码。毕竟,不同页面上要修剪的空白量可能会有所不同。
如果源文档不包含矢量图形,您可以简单地使用 iText 解析器包类。甚至已经有一个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一起使用会导致:

如您所见,代码根据页面上的文本(和位图图像)内容进行修剪。
要找到关于矢量图形的边界框,您基本上必须做同样的事情,但您必须扩展此处使用的解析器框架以通知其侦听器(TextMarginFinder
本质上是从解析器框架发送的绘图事件的侦听器)关于矢量图形操作也是如此。这很重要,特别是如果您还不熟悉 PDF 语法的话。
但是,如果要修剪的 PDF 不是太通用,但可能会被迫在相关位置包含一些文本或位图图形,则无论如何您都可以使用上面的示例代码(可能稍作改动)。
例如,如果您的 PDF 始终以顶部的文本开头并以底部的文本结尾,您可以更改 getOutputPageSize 以创建如下所示的结果矩形:
Rectangle result = new Rectangle(pageSize.getLeft(), finder.getLly(), pageSize.getRight(), finder.getUry());
这只会修剪顶部和底部的空白空间:

根据您的输入数据池和要求,这可能就足够了。
或者,您可以根据您对输入数据的了解使用其他一些启发式方法。如果您对文本的定位有所了解(例如,始终居中的标题和始终从左侧开始的其他文本),您可以轻松扩展TextMarginFinder
以利用这些知识。
最近(2015 年 4 月,iText 5.5.6-SNAPSHOT)的改进
当前的开发版本 5.5.6-SNAPSHOT 扩展了解析器包,还包括矢量图形解析。这允许扩展 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)
使用此类修剪空白会导致

这几乎是人们所希望的。
注意:上面的实现远非最佳。它甚至不正确,因为它包含了太多的所有曲线控制点。此外,它忽略了诸如线宽或楔形类型之类的东西。它实际上只是一个概念验证。
所有测试代码都在TestTrimPdfPage.java中。