C#でPDFファイルから画像を抜き出す 導入編 (iTextSharp)

iTextSharp というオープンソースライブラリを使うことで PDF ファイルを操作する事ができます。

PDFファイルに埋め込まれている画像を取り出してみようと思います。

 

iTextSharpのインストール

先ずは iTextSharp をインストールします。

インストール方法は「C#でPDFファイルを操作する 準備編 (iTextSharp)」を参照してください。

サンプルコード

using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using iTextSharp.text.pdf;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            PDFtoImage(@"C:\test.pdf", @"C:\images\");
        }

        static void PDFtoImage(string pdfName, string imgFolder)
        {
            var imgName = Path.GetFileNameWithoutExtension(pdfName);

            var pdf = new PdfReader(pdfName);
            var images = new List<PdfObject>();
            int n = pdf.NumberOfPages;
            for (int i = 1; i <= n; i++)
            {
                PdfDictionary pg = pdf.GetPageN(i);
                CollectImage(pg, images);
            }

            for (int i = 0; i < images.Count; ++i)
            {
                var obj = (PRIndirectReference)images[i];
                var pfdStream = (PRStream)pdf.GetPdfObject(obj.Number);
                byte[] bytes = PdfReader.GetStreamBytesRaw(pfdStream);

                var name = string.Format("{0}{1}_{2}.jpg", imgFolder, imgName, i);
                File.WriteAllBytes(name, bytes);
            }
        }

        static void CollectImage(PdfDictionary dic, List<PdfObject> images)
        {
            var res  = (PdfDictionary)PdfReader.GetPdfObject(dic.Get(PdfName.RESOURCES));
            var xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
            if (null != xobj)
            {
                foreach (PdfName name in xobj.Keys)
                {
                    PdfObject obj = xobj.Get(name);
                    if (obj.IsIndirect())
                    {
                        var tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
                        var type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
                        if (PdfName.IMAGE.Equals(type))
                        {
                            images.Add(obj);
                        }
                        else if (PdfName.FORM.Equals(type))
                        {
                            CollectImage(tg, images);
                        }
                        else if (PdfName.GROUP.Equals(type))
                        {
                            CollectImage(tg, images);
                        }
                    }
                }
            }
        }
    }
}

PDFを開く (22行目)

PdfReaderクラスのインスタンスを作成してPDFファイルを開きます。

PDF内の画像オブジェクトを収集する (23~29行目、42~70行目)

PDF内の様々な要素は PdfObject オブジェクトとして管理されています。

画像タイプの PdfObject オブジェクトを収集する為に「CollectImageメソッド」を作成しました。

 

PDF内の要素は階層構造となっている場合があるので、CollectImageメソッドは再帰的に呼び出されるようになっています。

データをbyte配列で取得して保存する (33~38行目)

GetStreamBytesRawメソッドで画像タイプの PdfObject からデータを byte配列で取得します。

取得したByte配列をそのままファイルへ書き出しています。

※残念ながらこれだけでは、一部の画像しか正しく取得する事は出来ません

※もう少し工夫が必要なようです

 

関連記事