PDF生成サンプル(Playwright)

# 目的
動的なHTMLをPDFに変換する方法を検討し、その実現にはPlaywrightを使用することにしました。

## Web API
インターフェースとしてはWebAPIを使用することにしました。
C#でのWebAPI作成を検討します。
現段階では、Visual Studioのテンプレートを利用することで開発労力を削減できると判断し、ASP.NETを選択しました。


## HTMLの作成
まずは、入力したパラメータによってHTMLを生成する方法を検討します。
HTMLの生成には、Razorを使用することにしました。
Razorは、C#のコードをHTMLに埋め込むことができるテンプレートエンジンです。
使用に際しては、nugetパッケージのMicrosoft.AspNetCore.Mvc.Razorで検索して、インストールします。

ソース作成にはgithubのchatbotの提案ソースをほぼそのまま使っています。
（正直、Razorの使い方がよくわかっていないので、このまま使うことにしました。）

```csharp:ReportController.cs
        private async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
        {
            var viewResult = _viewEngine.FindView(ControllerContext, viewName, false);
            if (viewResult.View == null)
            {
                throw new ArgumentNullException($"{viewName} does not match any available view");
            }

            using (var sw = new StringWriter())
            {
                var viewContext = new ViewContext(
                    ControllerContext,
                    viewResult.View,
                    new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                    {
                        Model = model
                    },
                    new TempDataDictionary(ControllerContext.HttpContext, _tempDataProvider),
                    sw,
                    new HtmlHelperOptions()
                );

                await viewResult.View.RenderAsync(viewContext);
                return sw.GetStringBuilder().ToString();
            }
        }
```



## PDFの生成
HTMLをPDFに変換する方法を検討します。
HTMLをPDFに変換する方法はいくつかありますが、今回はPlaywrightを使用することにしました。
使用に際しては、nugetパッケージのMicrosoft.Playwrightで検索して、インストールします。
Playwrightは、ブラウザの自動操作を行うためのライブラリです。
Playwrightを使用することで、ブラウザを操作してHTMLをPDFに変換することができます。

```csharp:ReportPdf.cs
using Microsoft.Playwright;

namespace net_pdf_sample.Models
{
    public class ReportPdf
    {
        public string Content { get; set; } = string.Empty;
        public string? HtmlFilePath { get; set; }
        public string? PdfFilePath { get; set; }
        private string _tempDirectory { get; set; }

        // コンストラクタでテンポラリディレクトリのパスを受け取る
        public ReportPdf(string tempDirectory)
        {
            _tempDirectory = tempDirectory;
        }

        // HTML文字列を受け取り、テンポラリディレクトリにHTMLファイルを作成する
        public async Task<string> CreateHtmlFile(string htmlContent)
        {
            return await Task<string>.Run(() =>
            {
                var tempPath = Path.Combine(_tempDirectory, $"{Path.GetRandomFileName()}.html");
                File.WriteAllText(tempPath, htmlContent);
                HtmlFilePath = tempPath;
                return tempPath;
            });
        }

        // HTMLファイルをPDFに変換する
        public async Task<string> ConvertToPdfAsync(string htmlFile)
        {
            using var playwright = await Playwright.CreateAsync();
            var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
            {
                Headless = true,
            });
            var pdfPath = Path.ChangeExtension(htmlFile, ".pdf");
            var page = await browser.NewPageAsync();
            await page.GotoAsync("file://" + htmlFile);
            await page.PdfAsync(new PagePdfOptions
            {
                Path = pdfPath
            });
            PdfFilePath= pdfPath;
            await browser.CloseAsync();
            return pdfPath;
        }
    }
}
```

上記のコードは、WebAPIから特定のレポートを生成し、そのレポートをPDF形式で返すための処理を示しています。



この機能をWebAPIから利用するためのコードを実装します。
`[HttpPost(Name = "Report")]`アトリビュートを使用してPOSTリクエストを受け取り、`PostReport`メソッドはタイトルと任意の引数を受け取り、HTMLテンプレートをレンダリングしてPDFファイルを生成します。

```csharp:ReportController.cs
        [HttpPost(Name = "Report")]
        public async Task<ActionResult<string>> PostReport(string title = "Title", string[]? args = null)
        {
            var report = new Models.Report()
            {
                Title = title,
                Args = args
            };
            // テンプレートをViewでレンダリングしてHTML文字列を作成する
            var html = await RenderViewToStringAsync("ReportTemplate", report);
            var reportPdf = new Models.ReportPdf(_tempDirectoryProvider.DirectoryName);
            var htmlFile = await reportPdf.CreateHtmlFile(html);
            var pdfFile = await reportPdf.ConvertToPdfAsync(htmlFile);
            return Ok(reportPdf);
        }

```


## まとめ

作成したソースコードはGitHubに登録しました。詳細は以下のリンクからご覧いただけます。
[WebAPI_PDF_sample - github](https://github.com/TakahiroSato68000/WebAPI_PDF_sample.git)