Html To PDF with Templater

BlazorTemplater, WkHtmlToPdfDotNet, HtmlToPDF, 工作紀錄

引言

用參數化的 Blazor 元件產生 html 再轉換成 PDF。

整合 BlazorTemplaterHaukcode.WkHtmlToPdfDotNet 兩個套件實作出可參數化送印報表的能力。

安裝套件

BlazorTemplater 產生 html, 可支援 Blazor 語法。當然也支援 Parameter 送入參數。 透過此套件就不用辛苦的手刻 html,尤其有明細清單時可以少一些工作。

WkHtmlToPdfDotNet 把 html 轉成 PDF。

開發平台

平台: NET8 IDE: Visual Studio 2022 框架: Blazor Web App - Server interactive

關鍵程式碼紀錄

報表元件.razor,可用 [Parameter] 帶入參數動態生成 html。

MyHtmlReport.razor
@using YourProject.Models

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
  <style>
    @@page {
      size: A4 portrait; /* 直印 */     
      margin-top: 20mm;
      margin-left: 10mm;
      margin-right: 10mm;
      margin-bottom: 10mm;
    }

    /* 浮水印 */
    .watermark {
      background-image: url("@(WatermarkImageUrl)");
    }

    table {
      border-collapse: collapse;
      width: 100%;
    }
    ...略...
    
  </style>
</head>
<body class="dev-preview watermark">

  <h1>HTML轉PDF測試:銀行簡介</h1>
  <div class="big-font" style="font-family:標楷體">@Title</div> @* <--- 參數化 *@

  <h1 class="break-page">顯示清單 <small style="color:darkorange;">(強制分頁控制)</small></h1>
  <ol>
    @foreach (var item in ItemList) @* <--- 明細清單參數化 *@
    {
      <li>@($"{item.ImageFileName} {item.ImageType}")</li>
    }
  </ol>
  ...略...
</body>
</html>

@code {
  [Parameter] public string Title { get; set; } = "這是報表標題";
  [Parameter] public List<ImageCropInfo> ItemList { get; set; } = new();
  [Parameter] public List<(string, string)> BarcodeList { get; set; } = new();
  [Parameter] public string WatermarkImageUrl { get; set; } = default!;
}

Html to PDF 轉換程序

SampleBiz.cs
/// <summary>
/// A4彩色直印
/// </summary>
public byte[] HtmlToPdf(string html)
{
  //# Define document to convert
  var doc = new HtmlToPdfDocument()
  {
    GlobalSettings = {
        ColorMode = ColorMode.Color,
        Orientation = Orientation.Portrait,
        PaperSize = PaperKind.A4,
        Margins = new MarginSettings() { Top = 26, Left = 10 },
      },
    Objects = {
      // 也有支援封面
      new CoverSettings()
      {
        HtmlContent = @"
<!DOCTYPE html>
<html>
...略...
</html>
"
      },
      // 報表內容
      new ObjectSettings() {
        PagesCount = true,
        HtmlContent = html,
        WebSettings = { DefaultEncoding = "utf-8", PrintMediaType = true },
        LoadSettings = new LoadSettings { ZoomFactor = 1.26 }, //1.26
        HeaderSettings = { 
          FontSize = 9, 
          Right = "Date: [date]", 
          Line = false, 
          Spacing = 0, // 2.812,
          HtmlUrl = $"{_navSvc.BaseUri}reportResource/cpaheader.html" // 必需是完整的URL
        },
        FooterSettings = { 
          FontSize = 9, 
          Right = "Page [page] of [toPage]", 
          Line = true, 
          Spacing = 2.812,
          HtmlUrl = ""
        },
      }
    }
  };

  //# Convert
  byte[] fileBlob = _pdfSvc.Convert(doc);
  return fileBlob;
}

產生報表主程式

YourPage.razor
@* ...略... *@
<MudButton OnClick=HandleComponentRendererReport>
  測試 Render Component to Report
</MudButton>

@code {

  async Task HandleComponentRendererReport()
  {
    try
    {
      f_loading = true;
  
      // 模擬資料
      List<ImageCropInfo> itemList = new()
      {
        new ImageCropInfo { ImageFileName = "image1.jpg", ... },
        new ImageCropInfo { ImageFileName = "image2.png", ... },
        new ImageCropInfo { ImageFileName = "image3.gif", ... },
      };
  
      List<(string, string)> barcodeList = new()
      {
        ("A10011", "data:image/png;base64,iVBORw0KGgoAAAANSUhE..."),
        ("B10011", "data:image/png;base64,iVBORw0KGgoAAAANSUhE..."),
        ("C10011", "data:image/png;base64,iVBORw0KGgoAAAANSUhE..."),
      };
  
      //# 產生 HTML 報表檔;可帶入參數
      string html = new ComponentRenderer<MyHtmlReport>()
                          .Set(m => m.Title, "唷~ 太神了傑克")
                          .Set(m => m.ItemList, itemList)
                          .Set(m => m.BarcodeList, barcodeList)
                          .Set(m => m.WatermarkImageUrl, $"{navSvc.BaseUri}image/watermark_draft.png")
                          .Render();
  
      // Convert to blob
      byte[] fileBlob = await Task.Run(() => bizSvc.HtmlToPdf(html)); // pdf file blob.
  
      //# 下載檔案
      string filename = $"ReportSample_{DateTime.Now.ToString("yyyyMMdd")}.pdf";
      await jsTool.DownloadFileAsync(filename, fileBlob);
    }
    catch (Exception ex)
    {
      dlgSvc.ShowAlert("出現例外!" + ex.Message);
    }
    finally
    {
      f_loading = false;
    }
  }
}

(EOF)

Last updated