DioDocs for PDF
PDFドキュメントの解析
機能 > PDFドキュメントの解析

DioDocs for PDFを使用すると、論理テキストとドキュメント構造を認識することにより、PDFドキュメントを解析できます。DioDocs for PDFを使用することで、プレーンテキスト、表、段落、要素などタグ付きPDFドキュメントのコンテンツ要素を抽出できます。

テキストの抽出

PDFからテキストを抽出するには

  1. GcPdfDocumentクラスの Load メソッドを使用してPDFドキュメントをロードします。
  2. Page クラスの GetText メソッドを使用してPDFの最後のページからテキストを抽出します。
  3. Graphics.DrawStringメソッドを使用して抽出したテキストを別のPDFドキュメントに追加します。
  4. GcPdfDocumentクラスの Save メソッドを使用してドキュメントを保存します。
    C#
    コードのコピー
    GcPdfDocument doc = new GcPdfDocument();
    
    FileStream fs = new FileStream("GcPdf.pdf",FileMode.Open,FileAccess.Read);
    doc.Load(fs);
    
    //PDFの最後のページからテキストを抽出します
    String text=doc.Pages.Last.GetText();
    
    //抽出したテキストを別のPDFドキュメントに追加します 
    GcPdfDocument doc1 = new GcPdfDocument();
    PointF textPt = new PointF(72, 72);
    doc1.NewPage().Graphics.DrawString(text, new TextFormat()
            { FontName = "ARIAL", FontItalic = true }, textPt);
    
    doc1.Save("NewDocument.pdf"); 
    
    Console.WriteLine("終了するには任意のキーを押してください。");  
    Console.ReadKey();
    

同様に、GcPdfDocument クラスの GetText メソッドを使用してドキュメントからすべてのテキストを抽出することもできます。

ITextMapを使用してテキストの抽出

DioDocs for PDFでは、DioDocs for PDFドキュメント内のページのテキストマップを表す ITextMap インターフェイスが提供されています。これにより、あるページ内のテキスト行の幾何学的位置を検索して、特定の位置にあるテキストを取得できます。

Page クラスの GetTextMap メソッドを使用して、ドキュメント内の特定のページに対するテキストマップを取得することができます。このメソッドは、ITextMap型のオブジェクトを返します。ITextMap 型は、テキスト範囲と範囲内のテキストを取得するためにオーバーロードされた4つの GetFragment メソッドを提供します。テキスト範囲は TextMapFragment クラスによって表され、範囲内の各テキスト行は TextLineFragment クラスによって表されます。

次の例では、GetFragment(out TextMapFragment range, out string text) オーバーロードを使用して、ページ上のすべてのテキスト行の幾何学的位置を取得し、GetFragment(MapPos startPos, MapPos endPos, out TextMapFragment range, out string text)オーバーロードを使用して、ページ内の特定の位置からテキストを取得しています。

C#
コードのコピー
//任意のPDFを開き、一時ドキュメントにロードし、マップを使用して一部のテキストを検索します
using (var fs = new FileStream("Test.pdf", FileMode.Open, FileAccess.Read))
{
    var doc1 = new GcPdfDocument();
    doc1.Load(fs);
    var tmap = doc1.Pages[0].GetTextMap();

    //ページ上の特定の幾何学的位置からテキストを取得します
    float tx0 = 2.1f, ty0 = 3.37f, tx1 = 3.1f, ty1 = 3.5f;
    HitTestInfo htiFrom = tmap.HitTest(tx0 * 72, ty0 * 72);
    HitTestInfo htiTo = tmap.HitTest(ty0 * 72, ty1 * 72);
    tmap.GetFragment(htiFrom.Pos, htiTo.Pos, out TextMapFragment range1, out string text1);
    tl.AppendLine(($"x={tx0:F2}\", y={ty0:F2}\", "width={tx1 - tx0:F2}\", height={ty1 - ty0:F2}\" " +
     $"の四角形内にテキストを検索しました。取得されたテキスト:");
    tl.AppendLine(text1);
    tl.AppendLine();

    //ページ上のすべてのテキストフラグメントとその位置を取得します
    tl.AppendLine("ページにあるすべてのテキストのリスト");
    tmap.GetFragment(out TextMapFragment range, out string text);
    foreach (TextLineFragment tlf in range)
    {
        var coords = tmap.GetCoords(tlf);
        tl.Append($"({coords.B.X / 72:F2}\",{coords.B.Y / 72:F2}\")でのテキスト:\t");
        tl.AppendLine(tmap.GetText(tlf));
    }
    //結果を印刷します
    tl.PerformLayout(true);
}
先頭に戻る

テキスト段落の抽出

DioDocs for PDFでは、ITextMapインターフェイスのParagraphsプロパティを使用して、PDFからテキスト段落を抽出すことができます。このプロパティは、テキストマップに関連付けられたITextParagraphオブジェクトのコレクションを返します。

PDFには、繰り返されるテキスト(例えば、太字で表示するために同じテキストを重ねる)を含む場合があります。ただし、DioDocs for PDFは、冗長な行を返さずにそのようなテキストを抽出します。また、セルに複数行のテキストがあるテーブルは、テキスト段落として正しく認識されます。

次の例では、PDFのすべてのテキスト段落を抽出する方法を示しています。

C#
コードのコピー
GcPdfDocument doc = new GcPdfDocument();
var page = doc.NewPage();
var tl = page.Graphics.CreateTextLayout();
tl.MaxWidth = doc.PageSize.Width;
tl.MaxHeight = doc.PageSize.Height;

//孤立およびウィドウコントロールのテキスト分割オプション
TextSplitOptions to = new TextSplitOptions(tl)
{
    MinLinesInFirstParagraph = 2,
    MinLinesInLastParagraph = 2,
};

//PDFを開き、一時ドキュメントにロードし、すべてのページテキストを取得します
using (var fs=new FileStream("Wetlands.pdf", FileMode.Open, FileAccess.Read))
{
    var doc1 = new GcPdfDocument();
    doc1.Load(fs);

    for (int i = 0; i < doc1.Pages.Count; ++i)
    {
        tl.AppendLine(string.Format("元のPDFの{0}ページの段落:", i + 1));

        var pg = doc1.Pages[i];
        var pars = pg.GetTextMap().Paragraphs;
        foreach (var par in pars)
        {
            tl.AppendLine(par.GetText());
        }
    }

    tl.PerformLayout(true);
    while (true)
    {
        //「rest」は、適合しなかったテキストを受け入れます
        var splitResult = tl.Split(to, out TextLayout rest);
        doc.Pages.Last.Graphics.DrawTextLayout(tl, PointF.Empty);
        if (splitResult != SplitResult.Split)
            break;
        tl = rest;
        doc.NewPage();
    }
    //参照のために、元のドキュメントを追加します
    doc.MergeWithDocument(doc1, new MergeDocumentOptions());
}
//PDFドキュメントを保存します
doc.Save(stream);
return doc.Pages.Count;

制限事項

テーブルからデータの抽出

GcPDFでは、PageクラスのGetTableメソッドを使用して、PDFドキュメント内のテーブルとして指定された領域からデータを抽出できます。このメソッドは、テーブル領域をパラメータとして受け取り、領域を解析して、行、列、セルのデータおよびそれらのテキストコンテンツを返します。また、TableExtractOptionsをパラメータとして渡すことで、列幅、行の高さ、行または列間の距離などのテーブルの書式設定オプションを指定することができます。

次の例では、PDFドキュメント内のテーブルからデータを抽出する方法を示します。

C#
コードのコピー
const float DPI = 72;
const float margin = 36;
var doc = new GcPdfDocument();
var tf = new TextFormat()
{
    Font = Font.FromFile(Path.Combine("segoeui.ttf")),
    FontSize = 9,
    ForeColor = Color.Black
};

var tfRed = new TextFormat(tf) { ForeColor = Color.Red };
var fs = File.OpenRead(Path.Combine("zugferd-invoice.pdf"));
{
    //テーブル境界 
    var tableBounds = new RectangleF(0, 3 * DPI, 8.5f * DPI, 3.75f * DPI);

    var page = doc.NewPage();
    page.Landscape = true;
    var g = page.Graphics;

    var tl = g.CreateTextLayout();
    tl.MaxWidth = page.Bounds.Width;
    tl.MaxHeight = page.Bounds.Height;
    tl.MarginAll = margin;
    tl.DefaultTabStops = 150;
    tl.LineSpacingScaleFactor = 1.2f;

    var docSrc = new GcPdfDocument();
    docSrc.Load(fs);

    var itable = docSrc.Pages[0].GetTable(tableBounds);

    if (itable == null)
    {
        tl.AppendLine($"指定された範囲にテーブルは見つかりませんでした。", tfRed);
    }
    else
    {
        tl.Append($"\nテーブルには、列が {itable.Cols.Count} 列と、行が {itable.Rows.Count} 行あります。データは以下のとおりです。", tf);
        tl.AppendParagraphBreak();
        for (int row = 0; row < itable.Rows.Count; ++row)
        {
            var tfmt = row == 0 ? tf : tf;
            for (int col = 0; col < itable.Cols.Count; ++col)
            {
                var cell = itable.GetCell(row, col);
                if (col > 0)
                    tl.Append("\t", tfmt);
                if (cell == null)
                    tl.Append("<セルなし>", tfRed);
                else
                    tl.Append(cell.Text, tfmt);
            }
            tl.AppendLine();
        }
    }
    TextSplitOptions to = new TextSplitOptions(tl) { RestMarginTop = margin, MinLinesInFirstParagraph = 2, MinLinesInLastParagraph = 2 };
    tl.PerformLayout(true);
    while (true)
    {
        var splitResult = tl.Split(to, out TextLayout rest);
        doc.Pages.Last.Graphics.DrawTextLayout(tl, PointF.Empty);
        if (splitResult != SplitResult.Split)
            break;
        tl = rest;
        doc.NewPage().Landscape = true;
    }
    //元のドキュメントを追加します 
    doc.MergeWithDocument(docSrc);
    doc.Save(stream);
メモ: 上記のサンプルコードで使用されているフォントファイルは、テーブルデータを取得するデモからダウンロードできます。

制限事項

タグ付きPDFからコンテンツの抽出

DioDocs for PDFは、PDFドキュメントの生成元であるドキュメントの論理構造を認識できます。構造認識の機能を利用してタグ付きPDFドキュメントから要素を抽出できます。

PDF仕様に基づいて、DioDocs for PDFはLogicalStructureクラスを使用して論理構造を認識します。LogicalStructureクラスは、PDF構造ツリーのタグに基づいて作成されたPDFドキュメントの解析された論理構造を表します。ElementクラスのStructElementプロパティを使用して、テーブル行のTR、見出しのH、段落のPなどの要素タイプを取得できます。

次のサンプルコードは、タグ付きPDFドキュメントから見出し、表、および目次要素を抽出する方法を示します。

C#
コードのコピー
static void ShowTable(Element e)
{
    List<List<IList<ITextParagraph>>> table = new List<List<IList<ITextParagraph>>>();
    
    // ネストされたすべての行、TRの要素を選択します
    void SelectRows(IList<Element> elements)
    {
        foreach (Element ec in elements)
        {
            if (ec.HasChildren)
            {
                if (ec.StructElement.Type == "TR")
                {
                    var cells = ec.Children.FindAll((e_) => e_.StructElement.Type == "TD").ToArray();
                    List<IList<ITextParagraph>> tableCells = new List<IList<ITextParagraph>>();
                    foreach (var cell in cells)
                        tableCells.Add(cell.GetParagraphs());
                    table.Add(tableCells);
                }
                else
                    SelectRows(ec.Children);
            }
        }
    }
    SelectRows(e.Children);

    // テーブルを表示します
    int colCount = table.Max((r_) => r_.Count);
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine($"Table: {table.Count}x{colCount}");
    Console.WriteLine($"------");
    foreach (var r in table)
    {
        foreach (var c in r)
        {
            var s = c == null || c.Count <= 0 ? string.Empty : c[0].GetText();
            Console.Write(s);
            Console.Write("\t");
        }
        Console.WriteLine();
    }
}

static void Main(string[] args)
{
    
    GcPdfDocument doc = new GcPdfDocument();

    using (var s = new FileStream("C1Olap QuickStart.pdf", FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        doc.Load(s);

        // LogicalStructureと最上位の親要素を取得します
        LogicalStructure ls = doc.GetLogicalStructure();
        Element root = ls.Elements[0];

        // すべての見出しを選択します
        Console.WriteLine("TOC:");
        Console.WriteLine("----");
        // 要素を反復処理し、すべての見出し要素を選択します
        foreach (Element e in root.Children)
        {
            string type = e.StructElement.Type;
            if (string.IsNullOrEmpty(type) || !type.StartsWith("H"))
                continue;
            int headingLevel;
            if (!int.TryParse(type.Substring(1), out headingLevel))
                continue;
            // 要素のテキストを取得します
            string text = e.GetText();
            if (string.IsNullOrEmpty(text))
                text = "H" + headingLevel.ToString();
            text = new string(' ', (headingLevel - 1) * 2) + text;
            Console.WriteLine(text);
            
        }

        // すべてのテーブルを選択します
        var tables = root.Children.FindAll((e_) => e_.StructElement.Type == "Table").ToArray();
        foreach (var t in tables)
        {
            ShowTable(t);
        }
    }
}

次のサンプルコードは、タグ付きPDFドキュメントの段落をハイライト表示の上、付箋を追加する方法を示します。
C#
コードのコピー
var doc = new GcPdfDocument();
using var s = File.OpenRead("C1Olap-QuickStart.pdf");
doc.Load(s);

// 段落をハイライト表示の上付箋をつけます。
void highlightParagraphs(IList items)
    {
        var color = Color.FromArgb(64, Color.Magenta);
        foreach (var e in items)
        {
            if (e.HasContentItems)
                foreach (var i in e.ContentItems)
                {
                    if (i is ContentItem ci)
                    {
                        var p = ci.GetParagraph();
                        if (p != null)
                        {
                            var rc = p.GetCoords().ToRect();
                            rc.Offset(rc.Width, 0);
                            TextAnnotation ta = new TextAnnotation()
                            {
                                UserName = "DioDocsDemo",
                                Rect = rc,
                                Page = ci.Page,
                                Contents = p.GetText(),
                            };
                            ci.Page.Graphics.DrawPolygon(p.GetCoords(), color, 1, null);
                        }
                    }
                }
            if (e.HasChildren)
                highlightParagraphs(e.Children);
        }
    }
    // LogicalStructureを取得し、それを使って段落をハイライト表示します。
    LogicalStructure ls = doc.GetLogicalStructure();
    highlightParagraphs(ls.Elements);

    // PDF ドキュメントを保存します。
    doc.Save(stream);

DioDocs for PDFを使用してタグ付きPDFを作成する方法について、タグ付きPDFを参照してください。