0
votes

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

1 Answers

2
votes

Yes, there is.

  1. Use IEventListener to 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.

  2. 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.

  3. This allows you to calculate the Rectangle that is occupied by the content that was rendered on a certain page.

  4. Now that you have this collection of Rectangles, you can easily check whether a given rectangle intersects with any of these rectangles. java.awt.Rectangle has a method for this called intersects

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