「C#でPDFファイルから画像を抜き出す 導入編 (iTextSharp)」ではPDFファイルに埋め込まれている画像を取り出す事に挑戦しました。
しかし、この方法では一部の画像しか正しく抜き出す事が出来ません。
抜き出す事が出来なかったものの1つに、モノクロTIFF画像があります。
今回はモノクロのTIFF画像を抜き出せるようにしてみます。
PDFの画像データの仕組み
画像データは辞書とストリームの2つで構成されています。
辞書には画像の情報が格納され、ストリームに画像が格納されます。
参照:https://ja.wikipedia.org/wiki/Portable_Document_Format
ストリームはいくつかの圧縮形式で格納する事ができます。
この中で「CCITTFaxDecode」という圧縮形式が、モノクロのTIFF画像で使われている圧縮形式になります。
モノクロTIFF画像の取り出し方
「C#でPDFファイルから画像を抜き出す 導入編 (iTextSharp)」では画像データのストリーム部分をそのままファイルへ書き出していました。
しかし、圧縮形式が「CCITTFaxDecode」の場合、ストリーム部分はそのままTIFF画像のデータ部として利用できますがヘッダー部分がありません。
そこで、画像データの辞書部分から必要な情報を取得し、LibTiff というオープンソースを使ってTIFF画像のヘッダー部分を作成するという方法をとります。
画像抜き出しのおさらい
まず必要なオープンソースをインストールします。
iTextSharp については「C#でPDFファイルを操作する 準備編 (iTextSharp)」を参照してください。
LibTiff はNuGetパッケージマネージャーから「BitMiracle.LibTiff.NET」をインストールします。
また、PDFファイルから画像オブジェクトを収取する部分は「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 tg = (PdfDictionary)PdfReader.GetPdfObject(images[i]); var filter = (PdfName)tg.Get(PdfName.FILTER); if (PdfName.CCITTFAXDECODE.Equals(filter)) { var wid = UInt32.Parse(tg.Get(PdfName.WIDTH).ToString()); var hei = UInt32.Parse(tg.Get(PdfName.HEIGHT).ToString()); var bpc = UInt32.Parse(tg.Get(PdfName.BITSPERCOMPONENT).ToString()); var cmp = Compression.CCITTFAX3; var parms = (PdfDictionary)tg.Get(PdfName.DECODEPARMS); var kobj = parms.Get(PdfName.K); if (null != kobj) { if (-1 == ((PdfNumber)kobj).IntValue) cmp = Compression.CCITTFAX4; } var name = string.Format("{0}{1}_{2}.tif", imgFolder, imgName, i); Tiff tiff = Tiff.Open(name, "w"); tiff.SetField(TiffTag.IMAGEWIDTH, wid); tiff.SetField(TiffTag.IMAGELENGTH, hei); tiff.SetField(TiffTag.COMPRESSION, cmp); tiff.SetField(TiffTag.BITSPERSAMPLE, bpc); tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1); tiff.WriteRawStrip(0, bytes, bytes.Length); tiff.Close(); } } } 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); } } } } } } }
変更したのは37~62行目です。
FILTER情報を取得する (38行目)
画像オブジェクトの辞書から圧縮形式を表す FILTER を取得します。
FILTER の値が「CCITTFaxDecode」の場合にTIFF画像として保存しています。
ヘッダー情報を収集する (41~51行目)
画像オブジェクトの辞書からTIFF画像のヘッダーとして必要な情報を取得します。
画像の幅、高さ、1色成分あたりのBit数の他に、圧縮形式の詳細について取得しています。
「CCITTFaxDecode」には「Group 3」と「Group 4」の2種類があり、
その情報が DECODEPARMS の K というキーワードを使って取得する事ができます。
K の値が -1 の時は「Group 4」です。
TIFFを作成する (53~61行目)
Tiffオブジェクトを作成し、SetFieldメソッドでヘッダー情報をセットしていきます。
さらに、WriteRawStripメソッドでストリーム部分をセットすればTIFF画像の完成です。
関連記事
- C#でPDFファイルを操作する 準備編 (iTextSharp)
- C#でPDFファイルから画像を抜き出す 導入編 (iTextSharp)
- C#でPDFファイルから画像を抜き出す モノクロTIFF編 (iTextSharp)
コメントをお書きください