C#でPDFを操作する

# 結合

複数のpdfファイルを結合して1つのpdfファイルにします。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //結合するファイル
            string[] files = { @"C:\work\a.pdf", @"C:\work\b.pdf" };

            //結合後の出力ファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            foreach (string file in files)
            {
                //ファイルを開く
                PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

                //１ページずつ出力ファイルに追加
                for (int i = 0; i < inputPdfDocument.PageCount; i++)
                {
                    outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);
                }

                //ファイルを閉じる
                inputPdfDocument.Close();
            }

            //出力
            outputPdfDocument.Save(@"C:\work\concat.pdf");
        }
    }
}
```

# 分割

pdfファイルを1ページ毎に分割します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //分割するファイル
            string file = @"C:\work\a.pdf";

            //ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //1ページずつ個別に保存
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                //出力するファイル
                PdfDocument outputPdfDocument = new PdfDocument();

                //ページ追加
                outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);

                //保存
                outputPdfDocument.Save(@"C:\work\split" + i.ToString() + ".pdf");
            }

            inputPdfDocument.Close();
        }
    }
}
```

# 回転

pdfファイルの全ページを回転します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //出力ファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                PdfPage page = inputPdfDocument.Pages[i];
                int deg, deg_next;

                //時計回り90度回転の場合
                deg = 90;

                //反時計回り90度回転の場合
                //deg = -90;

                //180度回転の場合
                //deg = 180;

                //角度設定
                deg_next = (page.Rotate + deg) % 360;
                deg_next += deg_next < 0 ? 360 : 0;
                page.Rotate = deg_next;

                outputPdfDocument.AddPage(page);
            }
            outputPdfDocument.Save(@"C:\work\rotate.pdf");

            inputPdfDocument.Close();
        }
    }
}
```

# ページ追加

pdfファイルの先頭に白紙のA4ページを追加します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //白紙を追加する元ファイル
            string file = @"C:\work\a.pdf";

            //元ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //出力ファイル作成
            PdfDocument outputPdfDocument = new PdfDocument();

            //白紙追加
            outputPdfDocument.AddPage(new PdfPage());
            outputPdfDocument.Pages[0].Size = PdfSharp.PageSize.A4;

            //元ファイル結合
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);
            }

            //元ファイルを閉じる
            inputPdfDocument.Close();

            //保存
            outputPdfDocument.Save(@"C:\work\addblankpage.pdf");
        }
    }
}
```


pdfファイルの先頭に白紙ページ（サイズは元ファイルの1ページ目と同じサイズ）を追加します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using PdfSharp.Drawing;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //白紙を追加する元ファイル
            string file = @"C:\work\a.pdf";

            //元ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //出力ファイル作成
            PdfDocument outputPdfDocument = new PdfDocument();

            //白紙追加
            outputPdfDocument.AddPage(new PdfPage());

            if (inputPdfDocument.Pages[0].CropBox.Height >= 200 && inputPdfDocument.Pages[0].CropBox.Width >= 200)
            {
                //CropBoxのサイズがある場合はCropBoxのサイズで設定
                outputPdfDocument.Pages[0].Width = getUint(inputPdfDocument.Pages[0].CropBox.Width);
                outputPdfDocument.Pages[0].Height = getUint(inputPdfDocument.Pages[0].CropBox.Height);
            }
            else
            {
                //CropBoxのサイズがない場合はMediaBoxのサイズで設定
                outputPdfDocument.Pages[0].Width = getUint(inputPdfDocument.Pages[0].MediaBox.Width);
                outputPdfDocument.Pages[0].Height = getUint(inputPdfDocument.Pages[0].MediaBox.Height);
            }

            //元ファイル結合
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);
            }

            //元ファイルを閉じる
            inputPdfDocument.Close();

            //保存
            outputPdfDocument.Save(@"C:\work\addblankpage.pdf");
        }
        private static XUnit getUint(double x)
        {
            int[] sizes = { 420, 595, 842, 1191, 1684, 2384, 3370, 516, 729, 1032, 1460, 2064, 2920, 4127 };

            foreach (int size in sizes)
            {
                if (System.Math.Abs(x - size) <= 1.0)
                {
                    return new XUnit(size);
                }
            }

            return new XUnit(x);
        }
    }
}
```

# ページレイアウト変更

pdfファイルをビューワで開いた時の見え方（ページレイアウト）を変更します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //出力ファイルを作成し元ファイルをコピー
            PdfDocument outputPdfDocument = new PdfDocument();
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);
            }
            inputPdfDocument.Close();

            //非連続、単一ページとして保存
            outputPdfDocument.PageLayout = PdfPageLayout.SinglePage;
            outputPdfDocument.Save(@"C:\work\output_layout_SinglePage.pdf");

            //非連続、見開きとして保存
            outputPdfDocument.PageLayout = PdfPageLayout.TwoPageLeft;
            outputPdfDocument.Save(@"C:\work\output_layout_TwoPageLeft.pdf");

            //非連続、見開き(1ページ目表紙)として保存
            outputPdfDocument.PageLayout = PdfPageLayout.TwoPageRight;
            outputPdfDocument.Save(@"C:\work\output_layout_TwoPageRight.pdf");

            //連続、単一ページとして保存
            outputPdfDocument.PageLayout = PdfPageLayout.OneColumn;
            outputPdfDocument.Save(@"C:\work\output_layout_OneColumn.pdf");

            //連続、見開きとして保存
            outputPdfDocument.PageLayout = PdfPageLayout.TwoColumnLeft;
            outputPdfDocument.Save(@"C:\work\output_layout_TwoColumnLeft.pdf");

            //連続、見開き(1ページ目表紙)として保存
            outputPdfDocument.PageLayout = PdfPageLayout.TwoColumnRight;
            outputPdfDocument.Save(@"C:\work\output_layout_TwoColumnRight.pdf");
        }
    }
}
```

# ページ2分割

pdfファイルの各ページを上下に2分割します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //出力ファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            //元ファイルのページを出力ファイルへコピー
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                PdfPage page1 = inputPdfDocument.Pages[i];

                //同じページを2回追加
                outputPdfDocument.AddPage(page1);
                outputPdfDocument.AddPage(page1);
            }
            inputPdfDocument.Close();

            //必要箇所のみ抜粋 PDFは左下が座標(0,0)
            for (int i = 0; i < outputPdfDocument.PageCount; i++)
            {
                PdfPage page = outputPdfDocument.Pages[i];

                if (i % 2 == 0)//奇数ページ
                {
                    page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, page.Height / 2, page.Width, page.Height / 2));
                }
                else//偶数ページ
                {
                    page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, 0, page.Width, page.Height / 2));
                }
            }

            //保存
            outputPdfDocument.Save(@"C:\work\cut_up_down.pdf");
        }
    }
}
```

pdfファイルの各ページを上下に2分割します。分割する際、ページ毎の回転角度を考慮して分割します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //出力ファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            //元ファイルのページを出力ファイルへコピー
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                PdfPage page1 = inputPdfDocument.Pages[i];

                //同じページを2回追加
                outputPdfDocument.AddPage(page1);
                outputPdfDocument.AddPage(page1);
            }
            inputPdfDocument.Close();

            //必要箇所のみ抜粋 PDFは左下が座標(0,0)
            for (int i = 0; i < outputPdfDocument.PageCount; i++)
            {
                PdfPage page = outputPdfDocument.Pages[i];

                //回転角度確認
                int deg = page.Rotate % 360;
                deg += deg < 0 ? 360 : 0;

                if (deg == 90 || deg == 270)
                {
                    if (i % 2 == 0)//奇数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(deg == 90 ? 0 : page.Width / 2, 0, page.Width / 2, page.Height));
                    }
                    else//偶数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(deg == 90 ? page.Width / 2 : 0, 0, page.Width / 2, page.Height));
                    }
                }
                else
                {
                    if (i % 2 == 0)//奇数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, deg == 180 ? 0 : page.Height / 2, page.Width, page.Height / 2));
                    }
                    else//偶数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, deg == 180 ? page.Height / 2 : 0, page.Width, page.Height / 2));
                    }
                }
            }

            //保存
            outputPdfDocument.Save(@"C:\work\cut_up_down.pdf");
        }
    }
}
```

pdfファイルの各ページを左右に2分割します。分割する際、ページ毎の回転角度を考慮して分割します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //出力ファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            //元ファイルのページを出力ファイルへコピー
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);
            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                PdfPage page1 = inputPdfDocument.Pages[i];

                //同じページを2回追加
                outputPdfDocument.AddPage(page1);
                outputPdfDocument.AddPage(page1);
            }
            inputPdfDocument.Close();

            //必要箇所のみ抜粋 PDFは左下が座標(0,0)
            for (int i = 0; i < outputPdfDocument.PageCount; i++)
            {
                PdfPage page = outputPdfDocument.Pages[i];

                //回転角度確認
                int deg = page.Rotate % 360;
                deg += deg < 0 ? 360 : 0;

                if (deg == 90 || deg == 270)
                {
                    if (i % 2 == 0)//奇数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, deg == 90 ? 0 : page.Height / 2, page.Width, page.Height / 2));
                    }
                    else//偶数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(0, deg == 90 ? page.Height / 2 : 0, page.Width, page.Height / 2));
                    }
                }
                else
                {
                    if (i % 2 == 0)//奇数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(deg == 180 ? page.Width / 2 : 0, 0, page.Width / 2, page.Height));
                    }
                    else//偶数ページ
                    {
                        page.MediaBox = new PdfRectangle(new PdfSharp.Drawing.XRect(deg == 180 ? 0 : page.Width / 2, 0, page.Width / 2, page.Height));
                    }
                }
            }

            //保存
            outputPdfDocument.Save(@"C:\work\cut_left_right.pdf");
        }
    }
}
```

# 文字追加

pdfファイルの各ページの下部に「ページ番号/総ページ番号」を追記します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using PdfSharp.Drawing;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //元ファイル
            string file = @"C:\work\a.pdf";

            //ファイルを開く
            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //出力するファイル
            PdfDocument outputPdfDocument = new PdfDocument();

            for (int i = 0; i < inputPdfDocument.PageCount; i++)
            {
                var pdfPage = outputPdfDocument.AddPage(inputPdfDocument.Pages[i]);

                //フォントの設定
                XFont xFont = new XFont("Arial", 14, XFontStyle.Regular);

                //入力するテキスト(ページ番号/総ページ番号)
                string text = (i + 1).ToString() + "/" + inputPdfDocument.PageCount.ToString();

                //テキストの追加
                XGraphics.FromPdfPage(pdfPage).DrawString(text, xFont, XBrushes.Black, new XRect(0, 0, pdfPage.Width, pdfPage.Height), XStringFormats.BottomCenter);
            }

            inputPdfDocument.Close();

            //保存
            outputPdfDocument.Save(@"C:\work\pagenum.pdf");

        }
    }
}
```
# 情報表示

pdfファイルの情報を表示します。

* [PDFsharp 1.50.5147](https://www.nuget.org/packages/PdfSharp/1.50.5147)

```cs
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Security;
using System;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string file = @"C:\work\a.pdf";

            PdfDocument inputPdfDocument = PdfReader.Open(file, PdfDocumentOpenMode.Import);

            //ドキュメント情報
            PdfDocumentInformation info = inputPdfDocument.Info;
            Console.WriteLine(info.Title);
            Console.WriteLine(info.Subject);
            Console.WriteLine(info.Keywords);
            Console.WriteLine(info.Owner);
            Console.WriteLine(info.Reference);
            Console.WriteLine(info.Author);
            Console.WriteLine(info.Creator);
            Console.WriteLine(info.Producer);

            //セキュリティ設定
            PdfSecuritySettings security = inputPdfDocument.SecuritySettings;
            Console.WriteLine(security.PermitAccessibilityExtractContent.ToString());
            Console.WriteLine(security.PermitAnnotations.ToString());
            Console.WriteLine(security.PermitAssembleDocument.ToString());
            Console.WriteLine(security.PermitExtractContent.ToString());
            Console.WriteLine(security.PermitFormsFill.ToString());
            Console.WriteLine(security.PermitFullQualityPrint.ToString());
            Console.WriteLine(security.PermitModifyDocument.ToString());
            Console.WriteLine(security.PermitPrint.ToString());

            inputPdfDocument.Close();
        }
    }
}
```

# 画像として保存

pdfファイルの各ページをPNGとして保存します。

* [PDFtoImage 2.2.0](https://www.nuget.org/packages/PDFtoImage/2.2.0)
* [SkiaSharp.Views.Desktop.Common](https://www.nuget.org/packages/SkiaSharp.Views.Desktop.Common)

```cs
using System.IO;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string file = @"C:\work\a.pdf";

            //ファイルを開く
            FileStream fs = new FileStream(file, FileMode.Open);

            //ページごとに画像として保存
            int page = 0;
            foreach (var img in PDFtoImage.Conversion.ToImages(fs))
            {
                page++;
                string outfile = @"C:\work\" + page.ToString() + ".png";
                SkiaSharp.Views.Desktop.Extensions.ToBitmap(img).Save(outfile, System.Drawing.Imaging.ImageFormat.Png);
            }

            //ファイルを閉じる
            fs.Close();
        }
    }
}
```

* [PdfiumViewer 2.13.0](https://www.nuget.org/packages/PdfiumViewer/2.13.0)
* [PdfiumViewer.Native.x86_64.v8-xfa 2018.4.8.256](https://www.nuget.org/packages/PdfiumViewer.Native.x86_64.v8-xfa/2018.4.8.256)

```cs
//x64
using PdfiumViewer;
using System;

namespace ConsoleApp1
{
    internal static class Program
    {
        [STAThread]
        static void Main()
        {
            string file = @"C:\work\a.pdf";
            PdfDocument inputpdfDocument = PdfDocument.Load(file);

            //解像度
            int dpi = 300;

            for (int i = 0; i < inputpdfDocument.PageCount; i++)
            {
                string outfile = @"C:\work\" + i.ToString() + ".png";
                int w = (int)inputpdfDocument.PageSizes[i].Width * dpi / 72;
                int h = (int)inputpdfDocument.PageSizes[i].Height * dpi / 72;
                System.Drawing.Image img = inputpdfDocument.Render(i, w, h, dpi, dpi, true);
                img.Save(outfile, System.Drawing.Imaging.ImageFormat.Png);
            }
        }
    }
}
```

# 表データ抽出

pdfファイル内にある全ての表のデータを抽出します。データはcsv形式で取得します。

* [Tabula 0.1.3](https://www.nuget.org/packages/Tabula/0.1.3)

```cs
using System;
using System.Linq;
using System.Collections.Generic;
using Tabula.Extractors;
using Tabula;
using UglyToad.PdfPig;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            using (PdfDocument document = PdfDocument.Open(@"C:\work\a.pdf", new ParsingOptions() { ClipPaths = true }))
            {
                ObjectExtractor oe = new ObjectExtractor(document);

                //出力用
                System.Text.StringBuilder sb = new System.Text.StringBuilder();

                //すべてのページで処理
                for (int i = 1; i <= document.GetPages().Count(); i++)
                {
                    PageArea page = oe.Extract(i);

                    IExtractionAlgorithm ea = new SpreadsheetExtractionAlgorithm();
                    List<Table> tables = ea.Extract(page);

                    //すべての表を処理
                    foreach (var table in tables)
                    {
                        //すべての行を処理
                        foreach (var row in table.Rows)
                        {
                            //すべてのセルを処理
                            foreach (var cell in row)
                            {
                                sb.Append(cell.GetText());
                                sb.Append(",");
                            }
                            sb.Append(System.Environment.NewLine);
                        }
                        sb.Append(System.Environment.NewLine);
                    }
                }

                //結果出力
                Console.WriteLine(sb.ToString());
            }
        }
    }
}
```

# テキスト抽出

pdfファイル内の全テキストを抽出します。

* [PdfiumViewer 2.13.0](https://www.nuget.org/packages/PdfiumViewer/2.13.0)
* [PdfiumViewer.Native.x86_64.v8-xfa 2018.4.8.256](https://www.nuget.org/packages/PdfiumViewer.Native.x86_64.v8-xfa/2018.4.8.256)

```cs
//x64
using PdfiumViewer;
using System;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string file = @"C:\work\a.pdf";
            PdfDocument inputpdfDocument = PdfDocument.Load(file);

            for (int i = 0; i < inputpdfDocument.PageCount; i++)
            {
                Console.WriteLine(inputpdfDocument.GetPdfText(i));
            }
        }
    }
}
```

* [PdfPig 0.1.7](https://www.nuget.org/packages/PdfPig/0.1.7)

```cs
using System;
using UglyToad.PdfPig;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            using (PdfDocument document = PdfDocument.Open(@"C:\work\a.pdf", new ParsingOptions() { ClipPaths = true }))
            {
                foreach (var page in document.GetPages())
                {
                    Console.WriteLine(page.Text);
                }
            }
        }
    }
}
```
# 参考URL

PDF座標の原点  
* [PDF内での座標の起点 | PDF](https://pdf-file.nnn2.com/?p=880)  

ボックスサイズ  
* [PDFの座標系、ページの大きさ、ヘッダーとフッター、トンボ](https://www.antenna.co.jp/pdf/reference/pdf-point.html)  
* [PDF Boxes : mediabox, cropbox, bleedbox, trimbox, artbox - Scribus Wiki](https://wiki.scribus.net/canvas/PDF_Boxes_:_mediabox,_cropbox,_bleedbox,_trimbox,_artbox)  

用紙サイズ  
* [用紙サイズとピクセル数の相対表](https://www.megasoft.co.jp/support/3dmhpro6/faq/img/trb0307.pdf)  

ページレイアウト  
* [PDFを開いたときのページレイアウト – Antenna House, Inc. - axf-document-info-2.pdf](https://www.antenna.co.jp/AHF/ahf_samples/ja/fo/pdf/axf-document-info-2.pdf)