Have an image that need to be painted on each page at a specific location. The constraint is we should paste the image only if the specific location is blank (not containing any text/images/tables, etc). Is there a way to do this?
1 Answers
Yes, there is.
Use
IEventListenerto parse a specific page in the pdf. You'll get notified about every event that the parser handles on the page. There are 3 kinds.PathRenderInfo,TextRenderInfo,ImageRenderInfo.All of these events can be queried for the coordinates of their content, as well as the transformation matrix that is applied before rendering them.
This allows you to calculate the
Rectanglethat is occupied by the content that was rendered on a certain page.Now that you have this collection of Rectangles, you can easily check whether a given rectangle intersects with any of these rectangles.
java.awt.Rectanglehas a method for this calledintersects
Then use some of the example code to position an image at an absolute position.
It's Friday, which is a wonderful day for everyone. So here's the code;
class FreeSpaceFinder implements IEventListener
{
private Collection<Rectangle> areas = new HashSet<>();
public FreeSpaceFinder(PdfPage page)
{
areas.clear();
PdfCanvasProcessor processor = new PdfCanvasProcessor(this);
processor.processPageContent(page);
}
public Collection<Rectangle> getOccupiedAreas()
{
return areas;
}
@Override
public void eventOccurred(IEventData iEventData, EventType eventType) {
if(eventType == EventType.RENDER_TEXT)
processText((TextRenderInfo) iEventData);
else if(eventType == EventType.RENDER_PATH)
processPath((PathRenderInfo) iEventData);
else if(eventType == EventType.RENDER_IMAGE)
processImage((ImageRenderInfo) iEventData);
}
private void processText(TextRenderInfo info)
{
for(TextRenderInfo characterRenderInfo : info.getCharacterRenderInfos())
{
com.itextpdf.kernel.geom.Rectangle charBoundingBox = new CharacterRenderInfo(characterRenderInfo).getBoundingBox();
areas.add(new Rectangle( (int) charBoundingBox.getX(),
(int) charBoundingBox.getY(),
(int) charBoundingBox.getWidth(),
(int) charBoundingBox.getHeight()));
}
}
private void processPath(PathRenderInfo info)
{
for(Subpath subpath : info.getPath().getSubpaths())
{
for(IShape segment : subpath.getSegments())
{
if(segment instanceof Line)
{
processLine(info, (Line) segment);
}
}
}
}
private float[] applyMtx(Matrix m, float[] point)
{
Matrix m2 = m.multiply(new Matrix(point[0], point[1]));
return new float[]{m2.get(Matrix.I31), m2.get(Matrix.I32)};
}
private void processLine(PathRenderInfo info, Line shape)
{
float[] p0 = applyMtx(info.getCtm(), new float[]{(float) shape.getBasePoints().get(0).getX(), (float) shape.getBasePoints().get(0).getY()});
int x0 = (int) p0[0];
int y0 = (int) p0[1];
float[] p1 = applyMtx(info.getCtm(), new float[]{(float) shape.getBasePoints().get(1).getX(), (float) shape.getBasePoints().get(1).getY()});
int x1 = (int) p1[0];
int y1 = (int) p1[1];
int w = java.lang.Math.abs(x0 - x1);
int h = java.lang.Math.abs(y0 - y1);
areas.add(new Rectangle(java.lang.Math.min(x0,x1), java.lang.Math.min(y0,y1), java.lang.Math.max(w, 1), java.lang.Math.max(h, 1)));
}
private void processImage(ImageRenderInfo info)
{
// #TODO
}
@Override
public Set<EventType> getSupportedEvents() {
return null;
}
}