我正在尝试使用 XSLT 和 CSS 将 XML 报告转换为 PDF。此 PDF 应在页脚中包含特定的页码,例如特定表格后的分页符。
根据我的发现,这可以使用 CSS3“at-attributes”来实现“分页媒体”(例如@page
)。但是,如果我理解正确,我可能很难找到解释这些属性以创建 PDF 的工具(更不用说它需要先从 XML 转换)。
我发现我可以使用paged.js
脚本让它在浏览器中运行,但它只有在我运行本地服务器(例如live-server
)时才有效,因为所有 Web 浏览器中都有一些本地文件限制。我可以(有点)使用命令行开关来克服这个问题,--allow-file-access-from-files
但它会在渲染完成之前打印文档(看起来浏览器不等待paged.js
脚本完成)。我尝试了不同的开关:chrome.exe --headless --disable-gpu --allow-file-access-from-files --run-all-compositor-stages-before-draw --virtual-time-budget=100000 --print-to-pdf="<destination>" "<source>"
. 也许节点引擎的一些开关可以提供帮助?
我的问题是如何使用具有免费商业许可的软件以编程方式将 XML 文件转换为 PDF,使用 XSLT 从 XML 中提取感兴趣的数据,并使用 CSS 将 PDF 格式化为正确的分页文档?我需要paged.js
完成它吗?
关于我的文件:在我的 XML 文件中,我引用了从 XML 中提取特定数据的本地 XSL 文件;他们删除重复项并按日期对它们进行排序。此 XSL 文件引用本地 CSS 文件以提供良好的格式和“分页”属性。XSL 还参考paged.js
了脚本和相关的 CSS,如脚本文档中所示。
我试过,在其他之间,,,,但weasyprint
没有成功。htmldoc
html5-to-pdf
wkhtmltopdf
我愿意接受任何建议。
编辑:我正在试验 XSL-FO(正如评论中所建议的那样),我不得不承认它工作得很好。在我看来,页面控件比 CSS 更具可读性。我现在看到的唯一问题是它需要额外的安装(Apache FOP 和 Java 运行时环境)。在我的场景中,在 .NET 中使用 FOP 会更好。
无论如何,我决定更详细地描述我使用 Chrome 作为渲染器的基于 CSS 的解决方案,因为它不需要额外的安装程序(或者至少我认为是这样)。我已经花了一些时间在它上面,似乎它几乎可以工作了。也许有人会发现问题出在哪里,然后它将成为使用 CSS 的分页媒体的一个非常好的解决方案。
完整transform.xsl
的文件是:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>Summary</title>
<link href="css/style.css" rel="stylesheet" type="text/css"/>
<link href="css/interface.css" rel="stylesheet" type="text/css"/>
<script src="js/paged.polyfill.js"/>
</head>
<body>
<h1>Summary</h1>
<table class="summary">
<tr><td>Total Quantity:</td><td><xsl:value-of select="count(Results/Result)"/></td></tr>
<tr><td>Passed:</td><td><xsl:value-of select="count(Results/Result[Status='Pass'])"/></td></tr>
<tr><td>Failed:</td><td><xsl:value-of select="count(Results/Result[Status='Fail'])"/></td></tr>
</table>
<br />
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Results">
<table class="results">
<tr>
<th>Serial Number</th>
<th>Test Result</th>
<th>Date</th>
<th>Time</th>
</tr>
<xsl:for-each select="Result">
<tr>
<xsl:attribute name="class"><xsl:value-of select="./Status"/></xsl:attribute>
<td><xsl:value-of select="./SerialNumber"/></td>
<td><xsl:value-of select="./Status"/></td>
<td><xsl:value-of select="./Date"/></td>
<td><xsl:value-of select="./Time"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
在我的style.css
我有@page
如下规则。我的原始文件中有更多内容,但在这里并不重要。
@page {
size: A4;
margin: 2cm 1cm;
@top-left {
content: "Summary Continued...";
font-size: 15px;
}
@bottom-center{
content: "Page " counter(page) "/" counter(pages);
font-size: 15px;
}
}
@page :first {
@top-left {
content: "";
}
}
数据存储在Reports.xml
(见下文)。在这个 xml 文件中,您可以放置任意数量的Result
字段。我的文件中有 200 个结果,但我在这里截断了它以使其更清晰。
<?xml version="1.0" encoding="iso-8859-1" ?><?xml-stylesheet type="text/xsl" href="xsl/transform.xsl"?>
<Results>
<Result ID="0">
<SerialNumber>8652280431</SerialNumber>
<Status>Fail</Status>
<Date>05-Mar-21</Date>
<Time>08:56:23</Time>
</Result>
<Result ID="1">
<SerialNumber>11124002643</SerialNumber>
<Status>Fail</Status>
<Date>05-Mar-21</Date>
<Time>08:56:23</Time>
</Result>
.
.
.
<Result ID="200">
<SerialNumber>6616001379</SerialNumber>
<Status>Fail</Status>
<Date>05-Mar-21</Date>
<Time>08:56:23</Time>
</Result>
</Results>
我的文件中也有,interface.css
如paged.polyfill.js
文档中所述(请参阅 参考资料transform.xsl
)。
当我Results.xml
在 Chrome 中使用命令打开时,chrome.exe --allow-file-access-from-files <file path>
它的工作方式为例外(见下图)。
当我尝试Results.xml
使用命令在 Chrome中打印我的时,chrome.exe --headless --disable-gpu --allow-file-access-from-files --run-all-compositor-stages-before-draw --virtual-time-budget=100000 --print-to-pdf=<destination> <source>
它会产生意外的结果。仅生成几页,总页数 ( counter(pages)
) 在页脚中返回 0(见下图)。
所以也许有人会想办法让它工作。
也许--js-flags
会成功?或者也许我应该添加/更改一些东西paged.polyfill.js
?