1

我有一个 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 包含矢量图形。

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

4

1 回答 1

3

由于没有发布实际的解决方案,这里有一些来自随附的itext-questions 邮件列表线程的指针:

  1. 由于您只想修剪页面,因此这不是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方法添加了另一个参数。它是页码。毕竟,不同页面上要修剪的空白量可能会有所不同。

  2. 如果源文档不包含矢量图形,您可以简单地使用 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一起使用会导致:

    在此处输入图像描述

    如您所见,代码根据页面上的文本(和位图图像)内容进行修剪。

  3. 要找到关于矢量图形的边界框,您基本上必须做同样的事情,但您必须扩展此处使用的解析器框架以通知其侦听器(TextMarginFinder本质上是从解析器框架发送的绘图事件的侦听器)关于矢量图形操作也是如此。这很重要,特别是如果您还不熟悉 PDF 语法的话。

  4. 但是,如果要修剪的 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中。

于 2013-11-26T08:47:24.877 回答