Search code examples
javapdfpdfboxviewport

How do I extract viewport from a pdf and modify an annotation's bounding rectangle according to the viewport?


I have implemented functionality to add link annotation to any pdf using pdfbox. It works well for most of pdfs, but for some pdfs it not placing markups at correct coordinates. And when I opened that pdf in some pdf editor, it gave me warning that the pdf contains an untitled viewport which might affect measurements for that pdf. So, I feel viewport is most probably causing the problem. Is there a way that I can modify the coordinates of markup according to viewport, so that it is placed at correct location in pdf. Here is a link to a pdf which contains the viewport.

According to Tilman's suggestion, I extracted the C entry from viewport's measure dictionary. And tried to modify rectangle's coordinate, but they are not getting added at the right location.Below is the code that I tried. Also, the viewport does not have effect on annotations, but it is causing problem when I try to draw something into the pdf.

COSArray vps = (COSArray)page.getCOSObject().getDictionaryObject(COSName.getPDFName("VP"));
if (vps != null)
{
    for (int v = 0; v < vps.size(); ++v)
    {

        COSDictionary vp = (COSDictionary)vps.getObject(v);
        PDViewportDictionary viewportDict = new PDViewportDictionary(vp);
        PDRectangle vpRect = viewportDict.getBBox();
        PDMeasureDictionary measureDict = viewportDict.getMeasure();
        PDRectlinearMeasureDictionary rectilinearDict = new PDRectlinearMeasureDictionary(measureDict.getCOSObject());
        bool pointLieInVP = UtilityClass.RectangleContainsPoint(new PointF(leftX, bottomY), vpRect);
        if (pointLieInVP)
        {
            COSArray xArray = (COSArray)measureDict.getCOSObject().getDictionaryObject(COSName.getPDFName("X"));
            float xScale = 1;
            if (xArray!=null)
            {
                xScale = ((COSFloat)(((COSDictionary)xArray.getObject(0)).getDictionaryObject(COSName.getPDFName("C")))).floatValue(); 
            }
            leftX /= xScale;
            rightX /= xScale;
            COSBase yObj = measureDict.getCOSObject().getDictionaryObject(COSName.getPDFName("Y"));
            if (yObj != null)
            {
                COSArray yArray = (COSArray)yObj;
                float yScale = ((COSFloat)(((COSDictionary)yArray.getObject(0)).getDictionaryObject(COSName.getPDFName("C")))).floatValue(); 
                bottomY /= yScale;
                topY /= yScale;
            }
            else
            {
                bottomY /= xScale;
                topY /= xScale;
            }
        }
    }
}

Here is the link to pdf markups are added without adjusting for viewports. The 5 red colored markups are added at bottom right end of the page. But they should have been placed over the link annotations in the pdf which are placed at correct positions. And here is the link for pdf , in which markups are placed after modifying their coordinates using the above code. The markups do not appear at all.


Solution

  • This code (which does not avoid ClassCastExceptions) will show you the viewports in each page:

    try (PDDocument doc = PDDocument.load(new File("S115-STRUCTURALHIGH ROOF FRAMING(WEST)ENLARGED PLANS.pdf")))
    {
        for (int p = 0; p < doc.getNumberOfPages(); ++p)
        {
            PDPage page = doc.getPage(p);
            COSArray vps = (COSArray) page.getCOSObject().getDictionaryObject(COSName.getPDFName("VP"));
            if (vps != null)
            {
                for (int v = 0; v < vps.size(); ++v)
                {
                    COSDictionary vp = (COSDictionary) vps.getObject(v);
                    PDRectangle rect = new PDRectangle((COSArray) vp.getDictionaryObject(COSName.BBOX));
                    System.out.println("Viewport " + vp.getString(COSName.NAME) + ": " + rect);
                }
            }
        }
    }
    

    How to adjust annotations is up to you... most likely, these should be inside the bbox. All you need to do is to adjust the rectangle of the annotations.