qt8gt0bxhw|20009F4EEE83|RyanMain|subtext_Content|Text|0xfbff200100000000b100000001000600
I was making some changes to a website where I had some PDF files and I wanted to be able to post thumbnail images of the PDF file. There were enough PDF files for me to want to take the lazy route and write some code to do it for me. I didn't want to go out and get some library that might have been able to do this for me, so I started poking around to see what I might already have to get the job done quickly.
Turns out that Adobe Acrobat Professional does expose quite a bit via COM. So I decided to see how far I could get with it to accomplish the task of generating thumbnail images for my PDF files. Well, guess what? It worked. Sort of. I could successfully open the documents and get a reference to a page in the PDF, the problem was then getting an image of the page for the thumbnail. But, the page class does have a CopyToClipboard method where you can copy the currently referenced page to the clipboard (and even specify the rect coordinates you want to copy). While I am not thrilled about using the clipboard, I couldn't find any other route to get the task done, so I decided to go with that. Once you get the page copied to the clioboard, it is easy enough to get the clipboard data as an image and use it however you need.
So I created an app that traversed the PDF images in a directory creating a thumbnail for each one. Pretty easy. Not the fastest thing ever, and it does use the clipboard so that rules out using this from a serviced context, but all in all it got the job done with flying colors. I put together a scaled down version of the app as a demo so I could post about it here. Here's the code in a simple form to generate a thumbnail image for the first page in the PDF file:
// add reference to "Acrobat" COM server defined in "acrobat.tlb"
// add using directives
using System.Runtime.InteropServices;
using System.Drawing;
//...
Acrobat.CAcroPDDoc doc = null;
Acrobat.CAcroPDPage page = null;
try
{
// instanciate adobe acrobat
doc = (Acrobat.CAcroPDDoc)new Acrobat.AcroPDDocClass();
if (doc.Open(@"C:\MyFile.pdf"))
{
if (doc.GetNumPages() > 0)
{
// get reference to page
// pages use a zero based index so 0 = page1
page = (Acrobat.CAcroPDPage)doc.AcquirePage(0);
// get dimensions of page and create rect to indicate full size
Acrobat.AcroPoint pt = (Acrobat.AcroPoint)page.GetSize();
Acrobat.CAcroRect rect = new Acrobat.AcroRectClass();
rect.Top = 0;
rect.Left = 0;
rect.right = pt.x;
rect.bottom = pt.y;
// copy current page to clipboard as image
page.CopyToClipboard(rect, 0, 0, 100);
// get image from clipboard as bitmap
IDataObject data = Clipboard.GetDataObject();
Bitmap bmp = (System.Drawing.Bitmap)data.GetData(DataFormats.Bitmap);
// calculate new height and width for thumbnail and maintain aspect ratio
int h = (int)((double)pt.y * ((double)100 / (double)pt.x));
int w = 100;
// create thumbnail
Image img = bmp.GetThumbnailImage(w, h, null, IntPtr.Zero);
img.Save(@"C:\MyThumbnail.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
catch
{
// if we get here and doc is null then we were unable to instanciate Acrobat
if (doc == null) MessageBox.Show("Acrobat is not installed. Adobe Acrobat is required.");
}
finally
{
if (page != null) Marshal.ReleaseComObject(page);
if (doc != null) Marshal.ReleaseComObject(doc);
}
One thing to mention. The CopyToClipboard method does allow you to specify “zoom“, so why not just use that to size the thumbnail? When you specify a smaller zoom ratio, the image copied to the clipboard is still the size of the entire original document but has the image of the page sized smaller in the corner. Not exatly what I wanted. So I get the full image and then size it myself. Also, if you wanted, you don't have to size it smaller if you also wanted a full size image of the page.
I threw together a small sample app using the code above to display and save any page from a PDF file.
Feel free to download the code for your own use (.NET 2.0 required and Acrobat 7.0 Pro is also required. Version 7.0 is only required since that is the version my interop DLL is generated from although this does work with other lower versions as well)