Process images extracted with PdfPig

Viewed 1196

Images extracted using PdfPig are the type of XObject Image or InlineImage (both inherit from IPdfImage). I would like to save and display them in a simple WPF application. In order to do so, I would need to have them in more accessible form, for example BitmapImage format. What is the correct way to achieve that? Library documentation does not help here and my miserable attempts were unsuccessful.

3 Answers

I haven't tested any of this, but it should at least put you on the right path if it doesn't work.

Looking at the PdfPig source on GitHub I can see both XObjectImage and InlineImage have a function TryGetPng. From the looks of it, I would assume that this byte array would match up with the contents of a normal PNG file, which means you should be able to load it straight into a BitmapImage.

Taking some code from this answer. Something like this might work:

InlineImage pdfImage;
byte[] png;

if (pdfImage.TryGetPng(out png))
{
    var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(png);
}

Note: both classes also have a TryGetBytes method, which might work in place of TryGetPng. I'm just not sure what format the output of TryGetBytes is in, so I'd be more confident with TryGetPng. Still, I'd try both if one doesn't work.

FWIW, by trial and error, my current approach is to start with TryGetPng and fall back to RawBytes if it fails. I then interpret the extracted bytes as a System.Drawing.Image. I don't use TryGetBytes at all. Here's my code (F#, but should be easy to convert to C#):

let bytes =
    match pdfImage.TryGetPng() with
        | true, bytes -> bytes
        | _ -> Seq.toArray pdfImage.RawBytes
use stream = new MemoryStream(bytes)
use image = Image.FromStream(stream)

I find the following code for me works in most cases. It simply tries all three options available to extract an image (TryGetPng, TryGetBytes and rawBytes) and converts those to an BmpSource.

    private static BitmapSource TryGetImage(IPdfImage image)
    {
        BitmapSource bmp;
        byte[] bytes;
        if (image.TryGetPng(out bytes))
        {
            bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
            Debug.WriteLine("Converted using TryGetPng.");
        }
        else
        {
            IReadOnlyList<byte> iroBytes;
            if (image.TryGetBytes(out iroBytes))
            {
                bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
                Debug.WriteLine("Converted using TryGetBytes.");
            }
            else
            {
                var rawB=image.RawBytes.ToArray<Byte>();
                Bitmap nbmp;
                using (var ms = new MemoryStream(rawB))
                {
                    nbmp = new Bitmap(ms);
                }
                bmp = ConvertBmpToBmpSource(nbmp);
                Debug.WriteLine("Converted using RawBytes.");
            }
        }
        return bmp;
    }

    public static BitmapSource ConvertBmpToBmpSource(Bitmap bitmap)
    {
        var bitmapData = bitmap.LockBits(
            new Rectangle(0, 0, bitmap.Width, bitmap.Height),
            System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

        var bitmapSource = BitmapSource.Create(
            bitmapData.Width, bitmapData.Height,
            bitmap.HorizontalResolution, bitmap.VerticalResolution,
            PixelFormats.Bgr24, null,
            bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);

        bitmap.UnlockBits(bitmapData);

        return bitmapSource;
    }
Related