2012年5月9日水曜日

C# iTextSharp でテキストを抽出

文字コードらしきものはどうやらCIDというAdobe固有の文字コードテーブルのようで、計算して復元できるような代物ではないらしい。その変換テーブル情報を CMap と言うらしい。
そこで xpdf というサイトにある xpdf-japanese.tar.gz というのを入手して、そのなかにある Adobe-Japan1-UCS2 というテキストファイル(拡張子なし)がどうも、今回のCMAPとして使えそうだ。
イメージとしては 読み込んだPDFの中にもそういったCMAP情報があって、それを元にコード変換していくのがスマートにも思えるが、PDFファイルの中にCMAP情報があるのか?またどのクラスモジュールで抜き出すのかがいまいち分からない。
PDFによっては他のCMAPファイルで文字コードを復元しなくちゃいけないんだろうが、どの情報を見てどのCMAPを使うかは今後の課題としよう・・・。
とりあえず、コーディングしてみた。
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace PDFtoHTML
{
    public partial class Form1 : Form
    {
        private string InPDF = @"C:hoge.pdf";
        private string InCMAP = @"C:\Adobe-Japan1-UCS2";

        private void button1_Click(object sender, EventArgs e) {
            // CMapの読込み
            iTextSharp.text.pdf.fonts.cmaps.CMap uCmap = new iTextSharp.text.pdf.fonts.cmaps.CMap();
            iTextSharp.text.pdf.fonts.cmaps.CMapParser uCMapParser = new iTextSharp.text.pdf.fonts.cmaps.CMapParser();
            StreamReader sr = new StreamReader(InCMAP, Encoding.GetEncoding("Shift-JIS"));
            uCmap = uCMapParser.Parse(sr.BaseStream);
            sr.Close();

            // iTextSharp::PdfReader PDFの読込み
            iTextSharp.text.pdf.PdfReader uReader = new iTextSharp.text.pdf.PdfReader(InPDF);

            Console.Write("□GetPageContent\n");
            for (int i = 0; i < uReader.NumberOfPages; i++) {
                Console.Write(string.Format(" Page No = {0}\n", (i + 1)));
                byte[] b = uReader.GetPageContent(i + 1);
                string s = iTextSharp.text.pdf.PdfEncodings.ConvertToString(b, PdfObject.TEXT_PDFDOCENCODING).Replace("\r\n", " ");
                Console.Write(string.Format("{0}\n", s));

                // テキスト部分を抽出
                System.Text.RegularExpressions.MatchCollection mc =
                    System.Text.RegularExpressions.Regex.Matches(s, @"<[0-9A-F]*?>|\(.*?\)");
                foreach (System.Text.RegularExpressions.Match m in mc)
                {
                    byte[] bv = new byte[2];
                    string ss = "";
                    if (m.Value[0] == '<')
                    {
                        for (int j = 1; j < (m.Value.Length - 1); j = j + 4)
                        {
                            bv[0] = Convert.ToByte(Convert.ToInt32(m.Value.Substring(j, 2), 16));
                            bv[1] = Convert.ToByte(Convert.ToInt32(m.Value.Substring(j + 2, 2), 16));
                            ss += uCmap.Lookup(bv, 0, 2);
                        }
                    }
                    else
                    {
                        ss += m.Value.Substring(1, m.Value.Length - 2);
                    }
                    Console.Write(string.Format("{0}\n", ss));
                }
            }

            // フォント一覧情報
            //List<object[]> uLisBaseFont = iTextSharp.text.pdf.BaseFont.GetDocumentFonts(uReader, (i));

            // Ending
            uReader.Close();
            Console.Write("■End\n");
        }
    }
}
これでUnicodeだが文字としてちゃんと取得できるようになった。
ついでにページ単位にフォント情報を一覧取得できる方法も分かったので、コメントアウトしてあるが掲載してみた。利用方法は不明だけど。。。
とりあえずはテキスト文字を抽出できるようになったみたいだが、どこからどこまでが1行分なのかがよくわからない。

元ネタ:http://yonaizumi.dip.jp/weblog/cappe/2011/01/citextsharp-2.html

0 件のコメント:

コメントを投稿