You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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的步骤

  1. 把上面的XML保存为orders.xml,XSLT保存为transform.xsl。
  2. 下载Apache FOP(开源的XSL-FO处理器),解压到本地。
  3. 打开命令行,执行以下命令:
    fop -xml orders.xml -xsl transform.xsl -pdf output.pdf
    
  4. 执行完成后,你就能得到符合要求的PDF文件output.pdf了。

内容的提问来源于stack exchange,提问作者Karthikeyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:54:47