从 XML 创建 PDF 文档





5.00/5 (24投票s)
从 XML 创建 PDF 文档。
引言
本文介绍如何使用 XML/XSL-FO 和开源库 FO.NET,通过混合输入参数、图像和静态本地化 string
来生成 PDF 文档。我认为这种方法非常适合自动化生成发票或与 Web 服务/服务器页面输入参数相关的简单动态文档/报告的过程。
背景
下图描述了此解决方案背后的思路
用户向 Web 服务提交其请求。该请求被序列化为 XML,并与静态本地化 XML 合并。结果通过 XSLT 样式表,输出是 XSL-FO 文档。XSL-FO (https://w3schools.org.cn/xslfo/) 是一种用于格式化 XML 数据以输出到屏幕、纸张或其他媒体的语言。它可以被 FO.NET (http://fonet.codeplex.com/) 解析,以便生成 PDF 文档。
架构概述
以下 UML 图描述了解决方案中最重要类之间的关系。
每个类及其职责的简要说明
IPdfPrintableDocument
:表示 PDF 可打印文档的基本接口Document
:表示一个非常简单的可打印文档XmlTransformationManager
:获取可打印文档实例并将其与相关的 XML 文化文件合并。输出是一个新的 XML 文件,该文件通过相关的 XSLT 文件。最终输出是可以被 FO.NET 解析的 XSL-FO 文档。PdfPrinterDriver
:借助FonetDriver
类处理 PDF 生成过程。ResourceManager
:负责访问本地化和转换文件。它利用CacheUtility
类将检索到的文件存储在缓存中,以加快后续请求的执行速度CacheUtility
:System.Web.Caching.Cache
类的简单包装器
设置
<pdfPrinterConfiguration>
<settings
xsltFolderPath="~/App_Data/Resources/XSLT/"
localizationXmlFolderPath="~/App_Data/Resources/XML/"
defaultCulture="en-US"
defaultDateFormat="MM/dd/yyyy"
pdfOutputFolder="~/GeneratedDocuments/"
/>
</pdfPrinterConfiguration>
在 web.config 中,有一个小配置节
xsltFolderPath
:告诉应用程序在哪里找到 XSLT 文件localizationXmlFolderPath
:告诉应用程序在哪里找到本地化文件defaultCulture
:默认应用程序文化defaultDateFormat
:DateTime
格式pdfOutputFolder
:告诉应用程序在哪里保存 PDF 文件。
创建一个简单的 WCF 服务
我决定使用一个简单的 WCF 服务来测试 PDF 打印机核心。首先,让我们创建一个 IPrintableDocument
类
[Serializable, DataContract(Namespace = "http://Schemas/PdfPrinter/Common")]
public class Document : IPrintableDocument
{
[DataMember(IsRequired = true)]
public string Description;
public string ToXml()
{
return ObjectXmlSerializer.SerializeObjectToXmlFormattedString(this);
}
}
然后,让我们编写一个带有请求和响应的简单 Web 服务
[ServiceBehavior(Namespace = "http://Schemas/PdfPrinter/WebServices")]
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequest request)
{
return PdfPrinterFacade.PrintPdf(request);
}
}
[MessageContract]
public class PdfPrinterResponse
{
[MessageHeader]
public string Info;
[MessageBodyMember]
public string Message;
}
[MessageContract]
public class PdfPrinterRequest
{
private Document _document;
[MessageBodyMember]
public Document Document
{
get { return _document; }
set { _document = value; }
}
}
最后,让我们实现我们的 XSLT 和本地化 XML 文件,并将它们命名为 Document
类。
我将 Document.xslt 放在 "~/App_Data/Resources/XSLT/" 文件夹下。这是一个简单的 XSLT,展示了 XSL-FO 的一些强大功能。然后,我编写了一个简单的本地化 XML 文件 Document.xml 并将其放在 "~/App_Data/Resources/XML/" 文件夹下。
运行代码
让我们使用 Document.Description = "Hello"
调用我们的 Web 服务。
所有输入数据都合并在一个 XML 文档中,如下所示
<PdfPrinter>
<Document>
<Description>Hello</Description>
</Document>
<culture language="en-US">
<label id="Footer" text="This documents has been auto-generated with Pdf Printer Service"/>
<label id="Page" text="Page " />
<label id="Of" text=" of " />
<label id="Message" text="You sent the following message:" />
<label id="EmptyMessage" text="- Empty message -" />
<label id="Date_Header_1" text="Date" />
<label id="LongWord_Header_2" text="Very Long Word" />
<label id="Decimal_Header_3" text="Decimal" />
<label id="Integer_Header_4" text="Integer" />
<label id="Total" text="Total" />
<label id="Date_Field_1" text="11/02/1984" />
<label id="LongWord_Field_2" text="ThisIsAVeryVeryLongBreakedWord" />
<label id="Decimal_Field_3" text="1234.5678" />
<label id="Integer_Field_4" text="3" />
</culture>
</PdfPrinter>
然后,XSLT 文件通过查询先前的 XML 内容来生成 XSL-FO 文档。它使用一个辅助扩展类 (PdfPrinter.Core.Common.XsltExtensionService
),该类简化了一些常见操作。
. . .
<xsl:variable name="logo"
select="utilityExtension:MapPath('~/App_Data/Resources/IMAGES/logo.jpg')"/>
<fo:block>
<fo:block>
<fo:external-graphic src="url('{$logo}')"
content-width="auto" content-height="auto"/>
</fo:block>
<fo:block padding-top="2pt">
<fo:block text-align="left" font-size="16pt">
<fo:inline font-weight="bold">
<xsl:value-of select="/PdfPrinter/culture/label[@id='Message']/@text"/>
</fo:inline>
</fo:block>
<fo:block text-align="left" padding-top="2pt" font-size="16pt">
<xsl:choose>
<xsl:when test="/PdfPrinter/Document/Description != ''">
<xsl:value-of select="/PdfPrinter/Document/Description"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/PdfPrinter/culture/label[@id='EmptyMessage']/@text"/>
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:block>
</fo:block>
. . .
最终结果由 FO.NET 解析,输出看起来非常棒!
结论
使用这种方法生成 PDF 文档的好处是,开发人员不必了解任何特定的 API,并且可以通过仅更改一些 XSLT 查询或添加新的 XSL-FO 语句来在运行时修改文档布局。
我希望我的工作对您有所帮助,并且使用起来愉快!