ゼロから始めるプログラム学習（C#）_008

##    はじめに

 プログラミング言語 __<font color="blue">　【C#『シーシャープ』】</font>__ を使用して
 プログラムを最初から学習するための記載。 


　　　前回：ゼロから始めるプログラム学習（C#）_007
　　　https://qiita.com/nekoozi/items/c07643603fd0cc92ca53


## <font color="green">PDFファイルをグレースケール化して結合する処理</font>
　今回は、複数のPDFファイルをグレースケール化し、 　それらを1つにマージを、以下のような流れで処理。

　１．PDFファイルを読み込む。

　２．各ページを画像として抽出し、グレースケール化する。

　３．グレースケール画像を新しいPDFファイルに再保存する。

　４．複数のPDFファイルを1つに結合する。

### 1･･･ <font color="blue">サンプル構成コード（PDFProcessor.cs）</font>
>PDFを画像に変換し、グレースケール化した後に再度PDFとして保存し、最終的に結合するコード


```C#
using OpenCvSharp;
using PdfiumViewer;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using System.Drawing.Imaging;

public static class PDFProcessor
{
    /// <summary>
    /// 選択された複数のPDFをグレースケール化してマージする
    /// </summary>
    /// <param name="inputFiles">入力PDFファイルのパス配列</param>
    /// <param name="outputFile">出力するマージ済PDFファイル</param>
    public static void ProcessAndMergePDFs(string[] inputFiles, string outputFile)
    {
        // 一時フォルダを作成して、そこで画像変換作業を行う
        string tempDir = Path.Combine(Path.GetTempPath(), "PDFGrayMerge_" + Guid.NewGuid().ToString());
        Directory.CreateDirectory(tempDir);

        List<string> tempPdfList = new();

        try
        {
            // 各PDFファイルに対して処理を行う
            foreach (var pdfPath in inputFiles)
            {
                string tempOutputPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(pdfPath) + "_gray.pdf");
                ConvertPdfToGrayscale(pdfPath, tempOutputPath);
                tempPdfList.Add(tempOutputPath);
            }

            // 全ての一時PDFを1つにマージ
            MergePdfs(tempPdfList.ToArray(), outputFile);
        }
        finally
        {
            // 一時ファイルを削除
            Directory.Delete(tempDir, true);
        }
    }

    /// <summary>
    /// PDFをページ単位でグレースケール画像に変換し、再PDF化する
    /// </summary>
    private static void ConvertPdfToGrayscale(string inputPath, string outputPath)
    {
        // PdfiumViewerを使ってPDFを開く
        using var document = PdfDocument.Load(inputPath);
        using var outputDocument = new PdfSharp.Pdf.PdfDocument();

        for (int i = 0; i < document.PageCount; i++)
        {
            // 画像としてPDFページを読み取る
            using var pageImage = document.Render(i, 300, 300, true);

            // 一時的に保存してOpenCvで処理
            string tempImagePath = Path.GetTempFileName();
            pageImage.Save(tempImagePath, ImageFormat.Png);

            // OpenCvSharpで画像を読み込み、グレースケール変換
            using var mat = Cv2.ImRead(tempImagePath);
            using var gray = mat.CvtColor(ColorConversionCodes.BGR2GRAY);

            // グレースケール画像を再保存（PDFSharpが読み込める形式）
            string grayImagePath = Path.GetTempFileName() + ".png";
            Cv2.ImWrite(grayImagePath, gray);

            // 変換した画像をPDFページとして追加
            var pdfPage = outputDocument.AddPage();
            using var gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(pdfPage);
            using var img = PdfSharp.Drawing.XImage.FromFile(grayImagePath);
            gfx.DrawImage(img, 0, 0, pdfPage.Width, pdfPage.Height);

            // 後始末
            File.Delete(tempImagePath);
            File.Delete(grayImagePath);
        }

        outputDocument.Save(outputPath);
    }

    /// <summary>
    /// 複数のPDFファイルを1つにマージする
    /// </summary>
    private static void MergePdfs(string[] pdfFiles, string outputFile)
    {
        using var outputDocument = new PdfDocument();

        foreach (var pdf in pdfFiles)
        {
            using var inputDocument = PdfReader.Open(pdf, PdfDocumentOpenMode.Import);

            // ページごとにコピーしてマージ
            for (int idx = 0; idx < inputDocument.PageCount; idx++)
            {
                var page = inputDocument.Pages[idx];
                outputDocument.AddPage(page);
            }
        }

        outputDocument.Save(outputFile);
    }
}


````

　説明

ProcessAndMergePDFs：
　複数のPDFファイルをグレースケール化し、それらを1つにマージするメイン処理です。

ConvertPdfToGrayscale：
　PDFをページ単位でグレースケール画像に変換し、それを再PDF化します。

MergePdfs：
　複数のPDFを1つにマージする処理です。


### 2･･･ <font color="blue">フォームから呼び出す処理（Form1.cs）</font>
>フォームアプリケーションでPDFファイル選択ダイアログを表示し、ユーザーが選択したPDFをグレースケール化して結合する処理のコード

```C#
private void btnConvertAndMerge_Click(object sender, EventArgs e)
{
    // ファイル選択ダイアログを表示して、複数PDFを選択
    using OpenFileDialog ofd = new OpenFileDialog();
    ofd.Filter = "PDFファイル (*.pdf)|*.pdf";
    ofd.Multiselect = true;

    if (ofd.ShowDialog() == DialogResult.OK)
    {
        // 出力ファイル名を選択
        using SaveFileDialog sfd = new SaveFileDialog();
        sfd.Filter = "PDFファイル (*.pdf)|*.pdf";
        sfd.FileName = "Merged_Gray.pdf";

        if (sfd.ShowDialog() == DialogResult.OK)
        {
            try
            {
                // PDFのグレースケール変換 & マージ処理を実行
                PDFProcessor.ProcessAndMergePDFs(ofd.FileNames, sfd.FileName);
                MessageBox.Show("PDFの変換と結合が完了しました！", "完了");
            }
            catch (Exception ex)
            {
                MessageBox.Show("エラーが発生しました: " + ex.Message);
            }
        }
    }
}

````

　説明
OpenFileDialog：
　ユーザーが選択したPDFファイルを取得するダイアログを表示します。

SaveFileDialog：
　出力先のファイル名を決定するダイアログを表示します。

ProcessAndMergePDFs：
　PDFの変換と結合処理を呼び出します。

--------------------------------------------------------------

　　　次回：作成中



## 参考文献

C#関連情報サイト様

OpenCvSharp公式GitHub
https://github.com/shimat/opencvsharp

PDFiumViewerドキュメント
https://pdfiumViewer.github.io

PdfSharp公式サイト
http://www.pdfsharp.net/