Images, Rectangles, and Matrices: A Conversation

Images, Rectangles, and Matrices: A Conversation

A colleague recently asked me if my Hoard of Code™ perchance contained an app demonstrating creating a thumbnail button which would take a user to the page pictured (for Adobe PDF Library version 10). I didn’t, but I threw one together based off of the old drawtomemory sample app.

Now, the drawtomemory sample app has since been retired from APDFL in preference for the RenderPage sample app. RenderPage itself has undergone some tweaking very recently to address some customer concerns, including demonstrating how to handle page rotations correctly.

So, I retested my latest addition to my Hoard of Code™ against a file I’d recently created with 4 pages – each page having the same image, shown the exact same on each page, but with different MediaBoxes and CropBoxes.

a screen capture showing incorrect thumbnails of different pages.

Whoops.  It should actually look like this:

a screen capture of thumbnails correctly rendered.

So, I ported my Version 10 code to the RenderPage code base, but with some additional tweaks. In version 1, I inserted a single new page for the thumbnails in front of the document. If there were more pages than there was room for the thumbnails, it croaked. In version 2, thumbnail pages are initially tacked on to the back of the document, and then transferred to the front.

I also wanted to decouple the input dimensions from the output dimensions. For rasterizing a page, the correct default dimensions are the Page’s CropBox. But what if you want to capture just part of the page? Also, just because you capture your rectangle at a given resolution does not necessarily mean that you want to display it at its original size going forward. You might want to display it bigger, or smaller.

So for version 2 of my Visual Table of Contents sample app, I restructured the code so I could do just that.

First things first, I added a parameter for RenderPage constructor so that we can specify an arbitrary area of the page to render.

Then I calculate where on the Thumbnail page I want to put the thumbnail image. It’s based on the original dimensions of the (arbitrary) input rectangle, but de-rotated, moved to the origin, scaled down to fit within another arbitrary rectangle, and then re-positioned to its final location on the thumbnail page. After those calculations are done, then I pass in that rectangle to GetPDEImage() which will calculate the matrix to position and scale the image to fit the dimensions of that rectangle.

After that comes the button-creation code. We first stuff the PDEImage into a Form XObject, which we will then use as the normal appearance of the push-button Widget Annotation. Lastly, we add the annotation as a form field, because this is the PDF format.

And on to the next page.

Now, in the RenderPage constructor, the parameter we added takes the place of the CropBox as the source of the updateRect, which means we can’t use PDPageGetFlippedMatrix() to supply us with the necessary matrix. Actually, you can, and (after the fact, of course), I found in my Hoard of Code™ that I had stashed a modified drawtomemory sample that did just that, but it’s messier.  This code calculates the flipped matrix based solely upon the updateRect. And page rotation. And desired resolution.

This matrix we’ve calculated is used to map page coordinates to the bitmap image that will be created. So we can use it to calculate the destination Rect of the bitmap image by transforming the updateRect. And we update the image attributes with the dimensions we thus calculated. Note that there is an ongoing conversation over whether this is actually the best approach to calculating the destRect and image dimensions.

The RenderPage GetPDEImage() method now requires a Rect. The calculation of the Image’s matrix is straightforward based on the Rect’s width and height – and it’s bottom left corner.

Is this the last word on RenderPage improvements? The Magic Eight Ball says “Don’t count on it“. But, I look forward to the conversation continuing.

Questions or comments? Leave a note below or contact us.

Leave a Reply

Your email address will not be published. Required fields are marked *