基于XML-XSL的PDF生成:每页仅显示4行需求实现
用XSL-FO实现XML转PDF每页4行数据
我来给你详细拆解怎么实现这个需求——要把XML里的大量数据行转成每页仅显示4行的PDF,核心是用**XSL-FO(XSL Formatting Objects)**做格式控制,配合XSLT的分组功能来拆分数据。下面是完整的实现方案:
核心思路
因为要生成PDF,我们需要先通过XSLT把原始XML转换成XSL-FO格式(这是专门用于排版的XML标准),再用Apache FOP这类工具把XSL-FO渲染成PDF。关键的一步是对XML中的数据行按每4个一组进行分组,每组对应PDF的一页内容。
完整示例代码
先补全你的XML示例(模拟10行数据)
<receipt> <order> <line_number>1</line_number> <product_code>S10</product_code> <product_name>Wireless Headphones</product_name> <quantity>2</quantity> <price>99.99</price> </order> <order> <line_number>2</line_number> <product_code>T20</product_code> <product_name>Smart Watch</product_name> <quantity>1</quantity> <price>199.99</price> </order> <order> <line_number>3</line_number> <product_code>L30</product_code> <product_name>Laptop Sleeve</product_name> <quantity>1</quantity> <price>29.99</price> </order> <order> <line_number>4</line_number> <product_code>P40</product_code> <product_name>Phone Case</product_name> <quantity>3</quantity> <price>14.99</price> </order> <order> <line_number>5</line_number> <product_code>C50</product_code> <product_name>Charger</product_name> <quantity>2</quantity> <price>24.99</price> </order> <order> <line_number>6</line_number> <product_code>M60</product_code> <product_name>Mouse</product_name> <quantity>1</quantity> <price>19.99</price> </order> <order> <line_number>7</line_number> <product_code>K70</product_code> <product_name>Keyboard</product_name> <quantity>1</quantity> <price>49.99</price> </order> <order> <line_number>8</line_number> <product_code>S80</product_code> <product_name>Speaker</product_name> <quantity>1</quantity> <price>79.99</price> </order> <order> <line_number>9</line_number> <product_code>B90</product_code> <product_name>Backpack</product_name> <quantity>1</quantity> <price>59.99</price> </order> <order> <line_number>10</line_number> <product_code>U100</product_code> <product_name>USB Drive</product_name> <quantity>5</quantity> <price>9.99</price> </order> </receipt>
XSLT样式表(实现分组+PDF排版)
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <fo:root> <!-- 定义A4页面模板,设置边距 --> <fo:layout-master-set> <fo:simple-page-master master-name="A4-page" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm"> <fo:region-body/> <fo:region-after region-name="footer" extent="1cm"/> </fo:simple-page-master> <fo:page-sequence-master master-name="A4-sequence"> <fo:repeatable-page-master-reference master-reference="A4-page"/> </fo:page-sequence-master> </fo:layout-master-set> <!-- 每4个order分为一组,每组生成一页 --> <xsl:for-each-group select="receipt/order" group-by="(position() - 1) idiv 4"> <fo:page-sequence master-reference="A4-sequence"> <fo:flow flow-name="xsl-region-body"> <!-- 页面标题 --> <fo:block font-size="16pt" font-weight="bold" text-align="center" margin-bottom="1cm"> Order Receipt (Page <xsl:value-of select="position()"/>) </fo:block> <!-- 数据表格 --> <fo:table table-layout="fixed" width="100%" border="1px solid black" border-collapse="collapse"> <!-- 表头 --> <fo:table-header> <fo:table-row> <fo:table-cell border="1px solid black" padding="5px"> <fo:block font-weight="bold">Line No.</fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px"> <fo:block font-weight="bold">Product Code</fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px"> <fo:block font-weight="bold">Product Name</fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px" text-align="right"> <fo:block font-weight="bold">Quantity</fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px" text-align="right"> <fo:block font-weight="bold">Price</fo:block> </fo:table-cell> </fo:table-row> </fo:table-header> <!-- 当前组内的数据行 --> <fo:table-body> <xsl:for-each select="current-group()"> <fo:table-row> <fo:table-cell border="1px solid black" padding="5px"> <fo:block><xsl:value-of select="line_number"/></fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px"> <fo:block><xsl:value-of select="product_code"/></fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px"> <fo:block><xsl:value-of select="product_name"/></fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px" text-align="right"> <fo:block><xsl:value-of select="quantity"/></fo:block> </fo:table-cell> <fo:table-cell border="1px solid black" padding="5px" text-align="right"> <fo:block>$<xsl:value-of select="price"/></fo:block> </fo:table-cell> </fo:table-row> </xsl:for-each> </fo:table-body> </fo:table> </fo:flow> <!-- 页脚:显示当前页码/总页数 --> <fo:static-content flow-name="footer"> <fo:block text-align="center" font-size="10pt"> Page <xsl:value-of select="position()"/> of <xsl:value-of select="ceiling(count(/receipt/order) div 4)"/> </fo:block> </fo:static-content> </fo:page-sequence> </xsl:for-each-group> </fo:root> </xsl:template> </xsl:stylesheet>
关键细节说明
- 分组逻辑:
group-by="(position() - 1) idiv 4"是核心——用当前数据行的位置减1后整除4,这样前4行(位置1-4)会被分到同一组,接下来4行(5-8)分到下一组,以此类推,完美实现每页4行的要求。 - 页面排版:定义了标准A4纸的页面布局,包含页眉、正文和页脚区域,表格样式清晰,数据对齐合理。
- 页码计算:总页数用
ceiling(count(/receipt/order) div 4)计算,也就是总行数除以4后向上取整,比如10行数据就会得到3页。
生成PDF的步骤
- 把上面的XML保存为
orders.xml,XSLT保存为transform.xsl。 - 下载Apache FOP(开源的XSL-FO处理器),解压到本地。
- 打开命令行,执行以下命令:
fop -xml orders.xml -xsl transform.xsl -pdf output.pdf - 执行完成后,你就能得到符合要求的PDF文件
output.pdf了。
内容的提问来源于stack exchange,提问作者Karthikeyan
相关产品推荐
相关产品推荐

