gpt4 book ai didi

java - 如何在有可用空间(如密度扫描仪)的 pdf 上插入图像或图章

转载 作者:行者123 更新时间:2023-12-01 17:03:43 25 4
gpt4 key购买 nike

我有一个 pdf 文件,我要在其中的所有页面上添加图章。

但是,问题是,图章被添加到每个页面的左上角。如果页面的该部分有文本,则标记会出现在文本上。

我的问题是,有没有什么方法可以让我阅读每一页,如果该部分没有文本,请添加图章,否则搜索最近的可用可用空间,就像密度扫描仪所做的那样?

我正在使用 IText 和 Java 1.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;
}

最佳答案

is there any method by which I can read each page and if there is no text in that part add the stamp else search for nearest available free space, just like what a density scanner does?

iText 不提供开箱即用的功能。不过,根据您想要避开的内容类型,您可能会考虑将页面渲染为图像并查找图像中的白点,或者使用尝试查找没有文本的位置的策略进行文本提取。

第一种选择是分析页面的渲染版本,这将是一个单独问题的焦点,因为必须首先选择图像处理库。

但是,在很多情况下,第一种选择并不是最好的选择。例如。如果您只想避开文本而不一定是图形(如水印),或者如果您还想避开不可见文本(通常可以在 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);
}

使用这个FreeSpaceFinder您可以通过如下方法找到具有给定最小尺寸的空白区域:

public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page) 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);
return finder.freeSpaces;
}

对于您的任务,您现在必须从返回的矩形中选择最适合您的矩形。

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

  • 它会忽略剪辑路径、渲染模式、颜色和覆盖对象。因此,它会考虑所有文本和所有位图图像,无论它们实际上是否可见。
  • 它不考虑 vector 图形(因为 iText 解析器包不考虑它们)。
  • 它还不是很优化。

应用于此 PDF 页面:

Sample Invoice

最小宽度为 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

或者,更直观地说,这里是页面上的矩形:

Sample Invoice with free rectangles at least 200x50 in size marked

纸平面是 vector 图形,因此被忽略。

当然,您也可以更改 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;
}

使用此距离测量,可以使用类似 Collection<Rectangle2D> rectangles 的代码来选择最近的矩形。和一个 Point2D point :

Rectangle2D best = null;
double bestDist = Double.MAX_VALUE;

for (Rectangle2D rectangle: rectangles)
{
double distance = distance(rectangle, point);
if (distance < bestDist)
{
best = rectangle;
bestDist = distance;
}
}

此后best包含一个最佳矩形。

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

Sample Invoice with free rectangles at least 200x50 in size marked and best rectangles filled

编辑两个

自 iText 5.5.6 起,RenderListener接口(interface)已扩展为 ExtRenderListener还可以收到有关路径构建和路径绘制操作的信号。因此,FreeSpaceFinder上面也可以扩展来处理路径:

//
// 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;

(FreeSpaceFinderExt.java)

使用此类,上面的结果改进为

Sample Invoice with free rectangles at least 200x50 in size marked and best rectangles filled, extended render listener

如您所见,现在也考虑了纸飞机和表格背景颜色。

关于java - 如何在有可用空间(如密度扫描仪)的 pdf 上插入图像或图章,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26464324/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com