Java 我如何在pdf上插入图像或图章,而pdf中有可用空间,如密度扫描仪

Java 我如何在pdf上插入图像或图章,而pdf中有可用空间,如密度扫描仪,java,itext,Java,Itext,我有一个pdf文件,我在其中添加了一个邮票的所有网页 但是,问题是,邮票被添加到每页的左上角。如果页面在该部分中有文本,则戳记将显示在文本上 我的问题是,有没有什么方法可以让我阅读每一页,如果那部分没有文本,就添加stamp-else搜索最近的可用空间,就像密度扫描仪一样 我正在使用IText和Java1.7 自由空间fider类和距离计算函数与公认答案中的相同 以下是我正在使用的编辑代码: // The resulting PDF file String RESULT = "K

我有一个pdf文件,我在其中添加了一个邮票的所有网页

但是,问题是,邮票被添加到每页的左上角。如果页面在该部分中有文本,则戳记将显示在文本上

我的问题是,有没有什么方法可以让我阅读每一页,如果那部分没有文本,就添加stamp-else搜索最近的可用空间,就像密度扫描仪一样

我正在使用IText和Java1.7

自由空间fider类和距离计算函数与公认答案中的相同

以下是我正在使用的编辑代码:

    // The resulting PDF file
    String RESULT = "K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release 1.pdf";

    // Create a reader
    PdfReader reader = new PdfReader("K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release.pdf");

    // Create a stamper
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));

    // Loop over the pages and add a footer to each page
    int n = reader.getNumberOfPages();

    for(int i = 1; i <= n; i++)
    {
          Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper); // minimum width & height of a rectangle

          Iterator itr = rectangles.iterator();

          while(itr.hasNext())
          {
                 System.out.println(itr.next());
          }

          if(!(rectangles.isEmpty()) && (rectangles.size() != 0))
          {
                 Rectangle2D best = null;

                 double bestDist = Double.MAX_VALUE;

                 Point2D.Double point = new Point2D.Double(200, 400);  

                 float x = 0, y = 0;

                 for(Rectangle2D rectangle: rectangles)
                 {
                       double distance = distance(rectangle, point);

                       if(distance < bestDist)
                       {
                              best = rectangle;

                              bestDist = distance;

                              x = (float) best.getX();

                              y = (float) best.getY();

                              int left = (int) best.getMinX();

                              int right = (int) best.getMaxX();

                              int top = (int) best.getMaxY();

                              int bottom = (int) best.getMinY();

                              System.out.println("x : " + x);
                              System.out.println("y : " + y);
                              System.out.println("left : " + left);
                              System.out.println("right : " + right);
                              System.out.println("top : " + top);
                              System.out.println("bottom : " + bottom);

                       }
                }

                getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i)); // 0, -1 indicates 1st row, 1st column upto last row and last column
        }

        else
             getFooterTable(i, n).writeSelectedRows(0, -1, 94, 140, stamper.getOverContent(i)); // bottom left corner
   }

   // Close the stamper
   stamper.close();

   // Close the reader
   reader.close();

   public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page, PdfStamper stamper) throws IOException
   {
         Rectangle cropBox = reader.getCropBox(page);

         Rectangle2D crop = new Rectangle2D.Float(cropBox.getLeft(), cropBox.getBottom(), cropBox.getWidth(), cropBox.getHeight());

         FreeSpaceFinder finder = new FreeSpaceFinder(crop, minWidth, minHeight);

         PdfReaderContentParser parser = new PdfReaderContentParser(reader);

         parser.processContent(page, finder);

         System.out.println("finder.freeSpaces : " + finder.freeSpaces);

         return finder.freeSpaces;
    }

    // Create a table with page X of Y, @param x the page number, @param y the total number of pages, @return a table that can be used as footer
   public static PdfPTable getFooterTable(int x, int y)
   {
         java.util.Date date = new java.util.Date();

         SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy");

         String month = sdf.format(date);
         System.out.println("Month : " + month);

         PdfPTable table = new PdfPTable(1);

         table.setTotalWidth(120);
         table.setLockedWidth(true);

         table.getDefaultCell().setFixedHeight(20);
         table.getDefaultCell().setBorder(Rectangle.TOP);
         table.getDefaultCell().setBorder(Rectangle.LEFT);
         table.getDefaultCell().setBorder(Rectangle.RIGHT);
         table.getDefaultCell().setBorderColorTop(BaseColor.BLUE);
         table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
         table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
         table.getDefaultCell().setBorderWidthTop(1f);
         table.getDefaultCell().setBorderWidthLeft(1f);
         table.getDefaultCell().setBorderWidthRight(1f);

         table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);

         Font font1 = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.BLUE);

         table.addCell(new Phrase("CONTROLLED COPY", font1));

         table.getDefaultCell().setFixedHeight(20);
         table.getDefaultCell().setBorder(Rectangle.LEFT);
         table.getDefaultCell().setBorder(Rectangle.RIGHT);
         table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
         table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
         table.getDefaultCell().setBorderWidthLeft(1f);
         table.getDefaultCell().setBorderWidthRight(1f);

         table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);

         Font font = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.RED);

         table.addCell(new Phrase(month, font));

         table.getDefaultCell().setFixedHeight(20);
         table.getDefaultCell().setBorder(Rectangle.LEFT);
         table.getDefaultCell().setBorder(Rectangle.RIGHT);
         table.getDefaultCell().setBorder(Rectangle.BOTTOM);
         table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
         table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
         table.getDefaultCell().setBorderColorBottom(BaseColor.BLUE);
         table.getDefaultCell().setBorderWidthLeft(1f);
         table.getDefaultCell().setBorderWidthRight(1f);
         table.getDefaultCell().setBorderWidthBottom(1f);

         table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);

         table.addCell(new Phrase("BLR DESIGN DEPT.", font1));

         return table;
   }
//生成的PDF文件
String RESULT=“K:\\DCIN\u TER\\DCIN\u EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\”+jtfSONo.getText().trim()+“\\PADR Release\\Final PADR Release 1.pdf”;
//创建一个阅读器
PdfReader reader=新PdfReader(“K:\\DCIN\u TER\\DCIN\u EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\”+jtfSONo.getText().trim()+“\\PADR Release\\Final PADR Release.pdf”);
//制作压模
PdfStamper stamper=新PdfStamper(读取器,新文件输出流(结果));
//在页面上循环并在每个页面上添加页脚
int n=reader.getNumberOfPages();
对于(int i=1;i
有没有什么方法可以让我阅读每页,如果那部分没有文本,就添加图章,或者像密度扫描仪一样搜索最近的可用空间


iTXT不提供框外的功能。但是,根据要躲避的内容,您可以考虑将页面呈现为图像并在图像中寻找白点,或者使用试图找到没有文本的位置的策略进行文本提取。 第一种选择是分析页面的呈现版本,这将是一个单独问题的焦点,因为必须首先选择图像处理库

不过,在很多情况下,第一种选择并不是最好的选择。例如,如果您只想避开文本,但不一定要避开图形(如水印),或者如果您还想避开不可见的文本(通常可以在PDF查看器中标记,因此会干扰您的添加)

第二种选择(使用iText的文本和图像提取功能)可能是这种情况下更合适的方法

下面是一个用于此类任务的示例
RenderListener

public class FreeSpaceFinder implements RenderListener
{
    //
    // constructors
    //
    public FreeSpaceFinder(Rectangle2D initialBox, float minWidth, float minHeight)
    {
        this(Collections.singleton(initialBox), minWidth, minHeight);
    }

    public FreeSpaceFinder(Collection<Rectangle2D> initialBoxes, float minWidth, float minHeight)
    {
        this.minWidth = minWidth;
        this.minHeight = minHeight;

        freeSpaces = initialBoxes;
    }

    //
    // RenderListener implementation
    //
    @Override
    public void renderText(TextRenderInfo renderInfo)
    {
        Rectangle2D usedSpace = renderInfo.getAscentLine().getBoundingRectange();
        usedSpace.add(renderInfo.getDescentLine().getBoundingRectange());
        remove(usedSpace);
    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo)
    {
        Matrix imageMatrix = renderInfo.getImageCTM();

        Vector image00 = rect00.cross(imageMatrix);
        Vector image01 = rect01.cross(imageMatrix);
        Vector image10 = rect10.cross(imageMatrix);
        Vector image11 = rect11.cross(imageMatrix);

        Rectangle2D usedSpace = new Rectangle2D.Float(image00.get(Vector.I1), image00.get(Vector.I2), 0, 0);
        usedSpace.add(image01.get(Vector.I1), image01.get(Vector.I2));
        usedSpace.add(image10.get(Vector.I1), image10.get(Vector.I2));
        usedSpace.add(image11.get(Vector.I1), image11.get(Vector.I2));

        remove(usedSpace);
    }

    @Override
    public void beginTextBlock() { }

    @Override
    public void endTextBlock() { }

    //
    // helpers
    //
    void remove(Rectangle2D usedSpace)
    {
        final double minX = usedSpace.getMinX();
        final double maxX = usedSpace.getMaxX();
        final double minY = usedSpace.getMinY();
        final double maxY = usedSpace.getMaxY();

        final Collection<Rectangle2D> newFreeSpaces = new ArrayList<Rectangle2D>();

        for (Rectangle2D freeSpace: freeSpaces)
        {
            final Collection<Rectangle2D> newFragments = new ArrayList<Rectangle2D>();
            if (freeSpace.intersectsLine(minX, minY, maxX, minY))
                newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), freeSpace.getWidth(), minY-freeSpace.getMinY()));
            if (freeSpace.intersectsLine(minX, maxY, maxX, maxY))
                newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), maxY, freeSpace.getWidth(), freeSpace.getMaxY() - maxY));
            if (freeSpace.intersectsLine(minX, minY, minX, maxY))
                newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), minX - freeSpace.getMinX(), freeSpace.getHeight()));
            if (freeSpace.intersectsLine(maxX, minY, maxX, maxY))
                newFragments.add(new Rectangle2D.Double(maxX, freeSpace.getMinY(), freeSpace.getMaxX() - maxX, freeSpace.getHeight()));
            if (newFragments.isEmpty())
            {
                add(newFreeSpaces, freeSpace);
            }
            else
            {
                for (Rectangle2D fragment: newFragments)
                {
                    if (fragment.getHeight() >= minHeight && fragment.getWidth() >= minWidth)
                    {
                        add(newFreeSpaces, fragment);
                    }
                }
            }
        }

        freeSpaces = newFreeSpaces;
    }

    void add(Collection<Rectangle2D> rectangles, Rectangle2D addition)
    {
        final Collection<Rectangle2D> toRemove = new ArrayList<Rectangle2D>();
        boolean isContained = false;
        for (Rectangle2D rectangle: rectangles)
        {
            if (rectangle.contains(addition))
            {
                isContained = true;
                break;
            }
            if (addition.contains(rectangle))
                toRemove.add(rectangle);
        }
        rectangles.removeAll(toRemove);
        if (!isContained)
            rectangles.add(addition);
    }

    //
    // members
    //
    public Collection<Rectangle2D> freeSpaces = null;
    final float minWidth;
    final float minHeight;

    final static Vector rect00 = new Vector(0, 0, 1);
    final static Vector rect01 = new Vector(0, 1, 1);
    final static Vector rect10 = new Vector(1, 0, 1);
    final static Vector rect11 = new Vector(1, 1, 1);
}
对于您的任务,您现在必须从返回的矩形中选择最适合您的矩形

请注意,此代码可能仍需要根据您的需求进行调整:

  • 它忽略剪辑路径、渲染模式、颜色和覆盖对象。因此,它考虑所有文本和所有位图图像,无论它们是否实际可见
  • 不考虑向量图形(因为ItExt解析器包不考虑它们)。
  • 它不是很优化
适用于此PDF页面:

最小宽度为200,高度为50,得到以下矩形:

x       y       w       h
000,000 000,000 595,000 056,423
000,000 074,423 595,000 168,681
000,000 267,304 314,508 088,751
000,000 503,933 351,932 068,665
164,296 583,598 430,704 082,800
220,803 583,598 374,197 096,474
220,803 583,598 234,197 107,825
000,000 700,423 455,000 102,396
000,000 700,423 267,632 141,577
361,348 782,372 233,652 059,628
或者,更直观地说,此处为页面上的矩形:

纸平面是矢量图形,因此被忽略

当然,您也可以将PDF呈现代码更改为不绘制要忽略的内容,并以可视方式绘制要忽略的原始不可见内容,然后使用位图图像分析

编辑

OP在他的评论中询问如何在
find
返回的矩形集合中找到距离给定点最近的矩形

首先不一定有最近的矩形,可能有多个

也就是说,可以选择一个最近的矩形,如下所示:

第一个需要计算点和矩形之间的距离,例如:

double distance(Rectangle2D rectangle, Point2D point)
{
    double x = point.getX();
    double y = point.getY();
    double left = rectangle.getMinX();
    double right = rectangle.getMaxX();
    double top = rectangle.getMaxY();
    double bottom = rectangle.getMinY();

    if (x < left) // point left of rect
    {
        if (y < bottom) // and below
            return Point2D.distance(x, y, left, bottom);
        if (y > top) // and top
            return Point2D.distance(x, y, left, top);
        return left - x;
    }
    if (x > right) // point right of rect
    {
        if (y < bottom) // and below
            return Point2D.distance(x, y, right, bottom);
        if (y > top) // and top
            return Point2D.distance(x, y, right, top);
        return x - right;
    }
    if (y < bottom) // and below
        return bottom - y;
    if (y > top) // and top
        return y - top;
    return 0;
}
在此之后,
best
包含一个最佳矩形

对于上面使用的示例文档,此方法返回页面角落和左右中心的彩色矩形:

编辑两个

自iText 5.5.6以来,
RenderListener
接口已扩展为
ExtRenderListener
,以向其发送有关路径构造和路径绘制操作的信号。因此,上面的
freespaceinder
也可以扩展以处理路径:

//
// Additional ExtRenderListener methods
//
@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)
        remove(currentPathRectangle);
    currentPathRectangle = null;

    return null;
}

@Override
public void clipPath(int rule)
{
    // TODO Auto-generated method stub

}

Rectangle2D.Float currentPathRectangle = null;
//
//额外的ExtRenderListener方法
//
@凌驾
公共void修改路径(PathConstructionRenderInfo renderInfo)
{
列表点=新的ArrayList();
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);
加(新向量(x,y,1));
添加(新向量(x+w,y,1));
加(新向量(x,y+h,1));
添加(新向量(x+w,y+h,1));
}
else if(renderInfo.getSegmentData()!=null)
{
对于(int i=0;iRectangle2D best = null;
double bestDist = Double.MAX_VALUE;

for (Rectangle2D rectangle: rectangles)
{
    double distance = distance(rectangle, point);
    if (distance < bestDist)
    {
        best = rectangle;
        bestDist = distance;
    }
}
//
// Additional ExtRenderListener methods
//
@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)
        remove(currentPathRectangle);
    currentPathRectangle = null;

    return null;
}

@Override
public void clipPath(int rule)
{
    // TODO Auto-generated method stub

}

Rectangle2D.Float currentPathRectangle = null;
for(int i = 1; i <= n; i++)
{
    Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper);
    ...
    x = (float) best.getX();
    y = (float) best.getY();
    ...
getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i));