欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

在App Service 透过Html 产生PDF

程序员文章站 2022-05-15 08:31:53
...

使用 TuesPechkin套件在App Service透过Html产生PDF的使用心得。

前言
最近自己和朋友都刚好有在App Service产生PDF的需求,在过去有很多套件都是基于 wkhtmltopdf来实做的,现在要在App Service上使用,是否会因为App Service架构和限制而产生问题:

执行绪问题。
是否会没有中文字形导致中文处理问题。
静态(图片、CSS、JavaScript)档案路径问题。
共用资源App Service Plan 无法有足够资源执行。
实做说明
安装 TuesPechkin 套件
经过一些测试,发现 TuesPechkin 这一个套件可以正常在App Service 使用,语法也算简单,因此最后决定使用这一套来产生PDF。

TuesPechkin 也是以wkhtmltopdf 为基础来开发的套件,我们可以直接透过NuGet 来安装套件,搜寻之后会有四个套件,其中TuesPechkin 为主要套件,其余三个则为对应CPU 核心的的wkhtmltopdf 套件,而为了之后方便,我选择AnyCPU 的套件。
在App Service 透过Html 产生PDF
新增 PdfHelper
新增一个静态类别 PdfHelper 来撰写转换Html 成PDF 的程式码。

/// <summary>
/// Pdf 轉換工具
/// </summary>
public static class PdfHelper
{
    /* 多執行續類型的程式需使用 ThreadSafeConverter 並且將 Converter 放置在 Static */
    private static IConverter converter =
        new ThreadSafeConverter(
            new RemotingToolset<PdfToolset>(
                new WinAnyCPUEmbeddedDeployment(
                    new TempFolderDeployment())));

    /// <summary>
    /// 將Html文字 輸出到PDF檔裡
    /// </summary>
    /// <param name="htmlText"></param>
    /// <returns></returns>
    public static byte[] ConvertHtmlTextToPDF(string htmlText)
    {
        if (string.IsNullOrEmpty(htmlText))
        {
            return null;
        }

        var document = new HtmlToPdfDocument
        {
            GlobalSettings = { },
            Objects = {
                new ObjectSettings {
                        HtmlText = htmlText
                }
                }
        };

        var result = converter.Convert(document);
        return result;
    }
}

多执行续类型的程式需使用ThreadSafeConverter 并且将Converter 放置在Static

准备要转换的Html
预先准备好要测试来转换的Html,这边需要注意的是:

CSS 中文字形需使用英文名称
图片路径可支援完整路径、Base64、实体档案路径
JavaScript 路径可支援完整路径、实体档案路径

<!DOCTYPE html>

<html lang="zh-tw" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>PDF 轉換測試</title>
    <style>
        body {
            margin: 0 auto;
            width: 21cm;
        }

        .text {
            font-size: x-large;
        }

        .text-pmingliu {
            font-family: "Microsoft PMingLiU";
        }

        .text-dfkai {
            font-family: DFKai-SB;
        }

        .text-jhenghei {
            font-family: "Microsoft JhengHei";
        }
    </style>
</head>
<body>
    <div>
        <!-- 中文測試 -->
        <h1>中文測試</h1>
        <p class="text text-pmingliu">新細明體</p>
        <p class="text text-dfkai">標楷體</p>
        <p class="text text-jhenghei">微軟正黑體</p>

        <!-- 圖片測試 -->
        <h1>圖片測試</h1>
        <img src="http://fakeimg.pl/250x100" />完整網址<br>
        <img src="data:image/png;base64,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" />Base64<br>
        <img src="{BasePath}/Images/250x100.png" />實體路徑<br>
        <!-- SVG 不支援 -->
        <svg id="svg-app-service" viewBox="0 0 50 50" width="100%" height="100%"> <path fill="#A0A1A2" d="M20.1,46.5H3.5V30h3.4c-0.4-1-0.6-2.1-0.6-3.3c0,0,0-0.1,0-0.2H0V50h23.6V36h-3.5V46.5z"></path> <path fill="#A0A1A2" d="M43.5,30h3v16.6H29.9V36.1h-3.5V50H50V26.5h-7.4c0.5,1,0.9,2,0.9,3.3C43.5,29.8,43.5,29.9,43.5,30z"></path> <path fill="#A0A1A2" d="M3.5,20V3.5h16.6v9.6c1-0.8,2.3-1.3,3.5-1.6V0H0v23.5h6.8C7.3,22.3,8,21,9,20.1L3.5,20L3.5,20z"></path> <path fill="#A0A1A2" d="M29.9,11.1V3.5h16.6v16.6h-7.3c0.3,1,0.5,2.2,0.5,3.4c0,0,0,0.1,0,0.1H50V0H26.4v10.9c0.3,0,0.5-0.1,0.8-0.1 C28.1,10.9,29,10.9,29.9,11.1z"></path> <path fill="#59B4D9" d="M40.8,29.7c0-2.1-1.7-3.7-3.7-3.7c-0.2,0-0.3,0-0.5,0c0.2-0.8,0.4-1.7,0.4-2.6c0-5.5-4.4-9.9-9.9-9.9 c-4.3,0-8,2.8-9.3,6.8c-0.7-0.2-1.4-0.4-2.2-0.4c-3.7,0-6.7,3-6.7,6.8c0,3.8,3,6.8,6.7,6.8c0,0,0,0,0,0v0h21.8l0,0 C39.3,33.3,40.8,31.7,40.8,29.7"></path> <path opacity="0.2" fill="#FFFFFF" d="M19.2,33.5c-0.9-0.9-1.5-2-1.8-3.3c-0.8-3.7,1.4-7.3,5.1-8.1c0.8-0.2,1.5-0.2,2.2-0.1 c0.3-3.4,2.4-6.5,5.5-8c-0.9-0.3-1.9-0.5-3-0.5c-4.3,0-8,2.8-9.3,6.8c-0.7-0.2-1.4-0.4-2.2-0.4c-3.7,0-6.7,3-6.7,6.8 c0,3.8,3,6.8,6.7,6.8c0,0,0,0,0,0v0H19.2z"></path> </svg>SVG

        <!-- JavaScript 測試 -->
        <p id="js-test"></p>
    </div>

    <script src="{BasePath}/Scripts/jquery-2.2.4.min.js"></script>
    <script>
        $(function () {
            $("#js-test").html("<b>JavaScript 測試</b>");
        });
    </script>
</body>
</html>

Action 中转换PDF

[HttpPost]
[ValidateInput(false)]  // 這邊是為了範例方便,正式環境不建議這樣使用,導致所有欄位都可以讀 Html
public ActionResult PDF(string Html)
{
    if (string.IsNullOrWhiteSpace(Html))
    {
        Html = System.IO.File.ReadAllText(Server.MapPath("~/App_Data/PDF.html"));
        // 取代 {BasePath} 成實體路徑
        Html = Html.Replace("{BasePath}", AppDomain.CurrentDomain.BaseDirectory);
    }

    var pdf = PdfHelper.ConvertHtmlTextToPDF(Html);

    return File(pdf, "application/pdf");
}

呈现结果
将程式都完成之后上传到App Service 测试,结果的画面呈现如下图:
在App Service 透过Html 产生PDF
其它
在测试的时候还有一点需要注意的, App Service Plan如果选择共用计算类型的定价层(免费、共用)会因为资源权限的关系无法正常转换,至少需要选择基础的定价层才有办法正常执行转换,这一点需要也是需要注意的地方。
结论
在App Service 是可以成功使用套件来转换Html 成PDF,但是仍有些需要注意的项目:

中文字形需使用英文名称,不然无法显示
静态档案需注意路径问题
App Service Plan 需要标准方案以上
此外,本文所写的范例为ASP.NET MVC ,套件也可能无法支援ASP.NET Core,因此未来还需要针对ASP.NET Core 的站台做测试才能知道是否可行或是需要用哪一个套件才可以。

相关标签: App