我的 multi-page 迭代 PDF 转换器应该使用哪种 XSLT:FO 布局?

What XSLT:FO layout should I use for my multi-page iterative PDF converter?

我正在使用 Apache 的 XML Graphics FOP 2.6 创建一个至少包含 2 页的 PDF 文档。没有最大页数。

每一页都使用完全相同的header。

第1页(见附件)

需要包含带有总框数的文本,总框数中包含的项目总数以及具有 1、2 或 3 行的 table。每行都有一张盒子的照片、它的名字和它包含的物品数量。至少会有一个盒子包含至少一件物品。

第 2 页

仅当有 4 个或更多个框时才存在,并且它包含第 1 页 table 的第 4、5、6、7、8 和 9 行(共 6 行)。如果有更多比 9 (= 3 + 6 X 1) 个框,需要有一个新页面将包含第 2 页的布局,但包含框 10,...,15 的行。将遵循此模式直到最后一个盒子.

第 3 页

将包含第一个盒子的照片、姓名和物品数量。下面会有一个table,一行包含列名,最多4行,对应第一个框的前4项。

第 4 页

如果第一个框有超过 4 个项目,将有新页面,称为第 4 页,仅包含 table 的延续,包括带有列名称的行。它总共有 7 个项目,如果第一个框有超过 11(= 4 + 7 X 1)个项目,则紧随其后的是另一页。以此类推,直到所有项目结束。

第5页(不附,以免重复)

将遵循与第 3 页相同的逻辑,但在本例中为第二个框,如果有第二个框。以此类推,直到所有框结束。

任何人都可以帮助我了解如何根据我的要求在 XSL 文件中构建 layout-master-set 吗?我不是在寻找完整的解决方案。仅针对一般布局结构。答案不必太详细或太定制以满足我的需要。如果需要我可以调整它。

<xsl:template match="/doc">
    <xsl:variable name="Logo"><xsl:value-of select="Logo"/></xsl:variable>
            ...

    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
             font-family="Nexus Sans Pro" font-weight="normal">
        <fo:layout-master-set>
            <fo:simple-page-master master-name="Boxes-A4" page-width="297mm" page-height="210mm"
                                   margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm">
                <fo:region-body region-name="xsl-region-body"/>
                <fo:region-before region-name="xsl-region-before" extent="70mm"/>
            </fo:simple-page-master>
            <fo:simple-page-master master-name="Items-A4" page-width="297mm" page-height="210mm"
                                   margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm">
                <fo:region-body region-name="xsl-region-body"/>
                <fo:region-before region-name="xsl-region-before" extent="70mm"/>
            </fo:simple-page-master>
        </fo:layout-master-set>

我已经尝试了上面的代码(使用 xsl:stylesheet version="1.0"),但未能适当地安排内容。 table 中不适合页面的行不会显示在下一页上,即使我用 break-before="page" aka page-break-before= 标记适当的行“总是”。 作为帮助,我可以将后端数据结构中的索引插入到库使用的参数中,这样我就知道每个元素的索引。

非常感谢。

Page 1

Page 2

Page 3

Page 4

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/doc">
        <fo:root font-family="Nexus Sans Pro" font-weight="normal">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="Boxes-A4" page-width="297mm" page-height="210mm"
                                       margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm">
                    <fo:region-body region-name="xsl-region-body"/>
                    <fo:region-before region-name="xsl-region-before" extent="70mm"/>
                </fo:simple-page-master>
                <fo:simple-page-master master-name="Items-A4" page-width="297mm" page-height="210mm"
                                       margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm">
                    <fo:region-body region-name="xsl-region-body"/>
                    <fo:region-before region-name="xsl-region-before" extent="70mm"/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <xsl:call-template name="boxes-template"/>
            <xsl:call-template name="items-template"/>
        </fo:root>
    </xsl:template>

    <xsl:template name="boxes-template">
        <fo:page-sequence master-reference="Boxes-A4" font-family="Nexus Sans Pro" font-weight="normal">
            <fo:static-content flow-name="xsl-region-before">
                <fo:block-container border-bottom-width="1pt"
                                    border-bottom-style="solid"
                                    border-bottom-color="rgb(220,220,220)"
                                    position="absolute" top="1mm" left="5mm" right="5mm" height="60mm">
                    <xsl:variable name="Logo" select="Logo"/>
                    <fo:block-container
                            background-image="url({$Logo})"
                            top="5mm" left="5mm" width="50mm" height="50mm"
                            background-repeat="no-repeat"
                            fox:background-image-width="50mm" fox:background-image-height="50mm"
                            absolute-position="absolute">
                        <fo:block/>
                    </fo:block-container>
                    <xsl:variable name="Stamp" select="Stamp"/>
                    <fo:block-container
                            background-image="url({$Stamp})"
                            top="5mm" right="5mm" width="50mm" height="50mm"
                            background-repeat="no-repeat"
                            fox:background-image-width="50mm" fox:background-image-height="50mm"
                            absolute-position="absolute">
                        <fo:block/>
                    </fo:block-container>
                    <fo:block-container position="absolute" top="8mm" left="80mm" width="120mm" height="42mm">
                        <fo:table table-layout="fixed" width="100%">
                            <fo:table-column column-width="100%"/>
                            <fo:table-body>
                                <fo:table-row>
                                    <fo:table-cell height="12mm">
                                        <fo:block font-family="Nexus Sans Pro Bold"
                                                  text-align="center" font-size="32pt"> Header Text
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                                <fo:table-row>
                                    <fo:table-cell display-align="after" height="10mm">
                                        <fo:block font-family="Nexus Sans Pro"
                                                  display-align="after" text-align="center" font-size="32pt"
                                                  color="rgb(233,113,28)">
                                            <xsl:value-of select="user"/>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>
                    <fo:block-container position="absolute" top="48mm" left="77mm" width="157mm" height="10mm">
                        <fo:table table-layout="fixed" width="100%">
                            <fo:table-body>
                                <fo:table-row>
                                    <fo:table-cell height="10mm">
                                        <fo:block font-family="Nexus Sans Pro" font-weight="normal" font-size="15pt"
                                                  color="rgb(128,128,128)">
                                            From:
                                            <xsl:value-of select="fromDate"/>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell height="10mm">
                                        <fo:block font-family="Nexus Sans Pro" font-weight="normal" font-size="15pt"
                                                  color="rgb(128,128,128)">
                                            To:
                                            <xsl:value-of select="toDate"/>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>
                </fo:block-container>
            </fo:static-content>

            <fo:flow flow-name="xsl-region-body">
                <fo:block-container position="absolute" top="75mm" left="110mm" width="120mm" height="20mm">
                    <fo:table table-layout="fixed" width="100%">
                        <fo:table-column column-number="1" column-width="50%"/>
                        <fo:table-column column-number="2" column-width="50%"/>
                        <fo:table-body>
                            <fo:table-row>
                                <fo:table-cell height="10mm">
                                    <fo:block
                                            font-family="Nexus Sans Pro Bold"
                                            font-size="15pt" color="rgb(35,31,32)">
                                        Total number of boxes:
                                    </fo:block>
                                </fo:table-cell>
                                <fo:table-cell height="10mm">
                                    <fo:block font-family="Nexus Sans Pro Bold"
                                              font-size="15pt" color="rgb(35,31,32)">
                                        <xsl:value-of select="totalNumberOfBoxes"/>
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                            <fo:table-row>
                                <fo:table-cell height="10mm">
                                    <fo:block font-family="Nexus Sans Pro Bold"
                                              font-size="15pt" color="rgb(35,31,32)">
                                        Total number of items:
                                    </fo:block>
                                </fo:table-cell>
                                <fo:table-cell height="10mm">
                                    <fo:block font-family="Nexus Sans Pro Bold"
                                              font-size="15pt" color="rgb(35,31,32)">
                                        <xsl:value-of select="totalNumberOfItems"/>
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                        </fo:table-body>
                    </fo:table>
                </fo:block-container>

                <xsl:for-each select="/doc/box">
                    <xsl:variable name="boxImageURL" select="boxImageURL"/>
                    <fo:block-container position="absolute" top="105mm" left="30mm" width="220mm" height="26mm">
                        <fo:table table-layout="fixed" width="100%">
                            <fo:table-column column-number="1" column-width="15%"/>
                            <fo:table-column column-number="2" column-width="70%"/>
                            <fo:table-column column-number="3" column-width="15%"/>
                            <fo:table-body>
                                <fo:table-row page-break-inside="auto"
                                              border-top-width="1pt"
                                              border-top-style="solid"
                                              border-top-color="rgb(220,220,220)"
                                              margin-bottom="2mm"
                                              height="24mm">
                                    <fo:table-cell>
                                        <xsl:if test="$boxImageURL != 'null'">
                                            <fo:block-container
                                                    background-image="url({$boxImageURL})"
                                                    top="110mm" right="15mm" width="15mm" height="20mm"
                                                    background-repeat="no-repeat" margin-top="2mm"
                                                    fox:background-image-width="15mm" fox:background-image-height="20mm">
                                                <fo:block/>
                                            </fo:block-container>
                                        </xsl:if>
                                    </fo:table-cell>
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                            <xsl:value-of select="boxTitle"/>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block text-align="left" font-size="16pt" color="rgb(35,31,32)">
                                            <xsl:value-of select="numberOfItems"/>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>
                </xsl:for-each>
            </fo:flow>
        </fo:page-sequence>
    </xsl:template>

    <xsl:template name="items-template">
        <xsl:variable name="Logo"><xsl:value-of select="Logo"/></xsl:variable>
        <xsl:variable name="Stamp"><xsl:value-of select="Stamp"/></xsl:variable>
        <xsl:variable name="user"><xsl:value-of select="user"/></xsl:variable>
        <xsl:variable name="fromDate"><xsl:value-of select="fromDate"/></xsl:variable>
        <xsl:variable name="toDate"><xsl:value-of select="toDate"/></xsl:variable>
        <xsl:for-each select="/doc/box">
            <xsl:variable name="boxImageURL" select="boxImageURL"/>
            <fo:page-sequence master-reference="Items-A4" font-family="Nexus Sans Pro" font-weight="normal">
                <fo:static-content flow-name="xsl-region-before">
                ... Same header as on the Box-A4
                </fo:static-content>

                <fo:flow flow-name="xsl-region-body">
                    <fo:block-container page-break-before="always"
                                        position="absolute" top="70mm" left="30mm" width="220mm" height="24mm">
                        <fo:table page-break-before="always" table-layout="fixed" width="100%">
                            <fo:table-column column-number="1" column-width="15%"/>
                            <fo:table-column column-number="2" column-width="85%"/>
                            <fo:table-body>
                                <fo:table-row margin-bottom="2mm"
                                              height="22mm">
                                    <fo:table-cell>
                                        <xsl:if test="$boxImageURL != 'null'">
                                            <fo:block-container
                                                    background-image="url({$boxImageURL})"
                                                    top="110mm" right="15mm" width="15mm" height="20mm"
                                                    background-repeat="no-repeat" margin-top="2mm"
                                                    fox:background-image-width="15mm" fox:background-image-height="20mm">
                                                <fo:block/>
                                            </fo:block-container>
                                        </xsl:if>
                                    </fo:table-cell>
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block  margin-top="4mm" text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                            <xsl:value-of select="boxTitle"/>
                                            <fo:block margin-top="4mm" text-align="left" font-size="16pt" color="rgb(35,31,32)">
                                                <xsl:value-of select="numberOfThings"/> things
                                            </fo:block>
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>

                    <fo:block-container position="absolute" top="100mm" left="30mm" width="220mm" height="24mm">
                        <fo:table page-break-before="always" table-layout="fixed" width="100%">
                            <fo:table-column column-number="1" column-width="60%"/>
                            <fo:table-column column-number="2" column-width="15%"/>
                            <fo:table-column column-number="3" column-width="25%"/>
                            <fo:table-body>
                                <fo:table-row border-top-width="1pt"
                                              border-top-style="solid"
                                              border-top-color="rgb(220,220,220)"
                                              margin-bottom="2mm"
                                              height="22mm">
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                            Item title
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                            Revision
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell display-align="center" height="20mm">
                                        <fo:block text-align="left" font-size="16pt" color="rgb(35,31,32)">
                                            Date completed
                                        </fo:block>
                                    </fo:table-cell>
                                </fo:table-row>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>

                    <fo:block-container position="absolute" top="125mm" left="30mm" width="220mm" height="24mm">
                        <fo:table page-break-before="always" table-layout="fixed" width="100%">
                            <fo:table-column column-number="1" column-width="60%"/>
                            <fo:table-column column-number="2" column-width="15%"/>
                            <fo:table-column column-number="3" column-width="25%"/>
                            <fo:table-body>
                                <xsl:for-each select="items/item">
                                    <fo:table-row keep-together.within-page="always"
                                                  break-after="page"
                                                  border-top-width="1pt"
                                                  border-top-style="solid"
                                                  border-top-color="rgb(220,220,220)"
                                                  margin-bottom="2mm"
                                                  height="22mm">
                                        <fo:table-cell display-align="center" height="20mm">
                                            <fo:block text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                                <xsl:value-of select="itemTitle"/>
                                            </fo:block>
                                        </fo:table-cell>
                                        <fo:table-cell display-align="center" height="20mm">
                                            <fo:block text-align="left" font-size="16pt" color="rgb(35,31,32)">
                                                <xsl:value-of select="revision"/>
                                            </fo:block>
                                        </fo:table-cell>
                                        <fo:table-cell display-align="center" height="20mm">
                                            <fo:block text-align="left"  font-size="16pt" color="rgb(35,31,32)">
                                                <xsl:value-of select="dateCompleted"/>
                                            </fo:block>
                                        </fo:table-cell>
                                    </fo:table-row>
                                </xsl:for-each>
                            </fo:table-body>
                        </fo:table>
                    </fo:block-container>
                </fo:flow>
            </fo:page-sequence>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

我的简化parameters.xml(缺少几行):

<doc>
    <Logo>ELogo.jpg</Logo>
    <Stamp>Stamp.jpg</Stamp>
    <backgroundImageURL>bkg.jpg</backgroundImageURL>
    <user>John Richard Edgar Bowens-Robins III</user>
    <fromDate>1st March 2021</fromDate>
    <toDate>31 November 2021</toDate>
    <totalNumberOfBoxes>5</totalNumberOfBoxes>
    <totalNumberOfItems>19</totalNumberOfItems>
    <box>
        <boxImageURL>box1.jpg</boxImageURL>
        <boxTitle>JREBR's Box 1</boxTitle>
        <items>
            <item>
                <itemTitle>The Nice Item 11</itemTitle>
            </item>
            <item>
                <itemTitle>The Nice Item 12</itemTitle>
            </item>
            <item>
                <itemTitle>The Nice Item 13</itemTitle>
            </item>
        </items>
    </box>
    <box>
        <boxImageURL>box2.jpg</boxImageURL>
        <boxTitle>JREBR's Box 2</boxTitle>
        <items>
            <item>
                <itemTitle>The Nice Item 21</itemTitle>
            </item>
            <item>
                <itemTitle>The Nice Item 22</itemTitle>
            </item>
            <item>
                <itemTitle>The Nice Item 23</itemTitle>
            </item>
        </items>
    </box>
</doc>

使用 fo:table,并为每个 fo:table-row 指定 height (https://www.w3.org/TR/xsl11/#height) 和一个 suitable 值,这将使您获得所需的数量每页行数。

您可能还需要指定 keep-together.within-page="always" 以避免破坏 table 行。 FOP 在 fo:table-row(或 fo:table-cell)上可能支持也可能不支持。

你所谓的“第 1 页”、“第 2 页”……你的意思是这样的:

第 1 部分是所有框。这可能是 1 到 x 页,具体取决于盒子的数量。所有页面模板都相同,因此这是一个页面序列和一个页面母版。

第 2 到第 n 部分是列出所有项目的每个框的页面序列。所有这些页面序列都是相同的(一个简单的页面母版)。

您没有解释 header 中的照片 1 和照片 2 是什么,但我认为它们在每一页上都是相同的。如果这不是真的并且“盒子”和“物品”不同,那么您可能有不同的 headers.

所以你只有两个 simple-page-masters 一个叫做“盒子”,一个叫做“物品”。您只需为框创建引用 simple-page-master 的“框”page-sequence。您没有提供样本 XML,所以为了简单起见,我们只说它看起来像这样:

<order>
<box>
    <name>Box 1</name>
    <items>
        <item>Item 1:1</item>
        <item>Item 1:2</item>
        <item>Item 1:3</item>
    </items>
</box>
<box>
    <name>Box 2</name>
    <items>
        <item>Item 2:1</item>
        <item>Item 2:2</item>
        <item>Item 2:3</item>
    </items>
</box>
<box>
    <name>Box 3</name>
    <items>
        <item>Item 3:1</item>
        <item>Item 3:2</item>
        <item>Item 3:3</item>
    </items>
</box>
</order>

然后这个简单的 XSL 会为这些盒子和这些盒子中的项目抛出页面:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
    <fo:root>
        <fo:layout-master-set>
            <fo:simple-page-master master-name="boxes" page-width="7in" page-height="4in">
                <fo:region-body region-name="body" margin-top="0.5in" margin-bottom="0.5in" margin-left="0.5in" margin-right="0.5in"/>
            </fo:simple-page-master>
            <fo:simple-page-master master-name="items" page-width="7n" page-height="4in">
                <fo:region-body region-name="body" margin-top="0.5in" margin-bottom="0.5in" margin-left="0.5in" margin-right="0.5in"/>
            </fo:simple-page-master>
        </fo:layout-master-set>
        <xsl:call-template name="boxes"/>
        <xsl:call-template name="items"/>
    </fo:root>
</xsl:template>
<xsl:template name="boxes">
    <fo:page-sequence master-reference="boxes">
        <fo:flow flow-name="body">
        <!-- Output the table of boxes -->
        <xsl:for-each select="/order/box">
            <fo:block>
                <xsl:value-of select="name"/>
            </fo:block>
        </xsl:for-each>
        </fo:flow>
    </fo:page-sequence>
</xsl:template>

<xsl:template name="items">
    <xsl:for-each select="/order/box">
        <fo:page-sequence master-reference="items">
            <fo:flow flow-name="body">
            <xsl:for-each select="items/item">
                <fo:block>
                    <xsl:value-of select="."/>
                </fo:block>
            </xsl:for-each>
            </fo:flow>        
        </fo:page-sequence>
    </xsl:for-each> 
</xsl:template>
</xsl:stylesheet>

这导致:

将此与@Tony Graham 关于制作表格的建议相结合,您便拥有了整个布局。如果您想知道它如何与自动页面流一起工作,这里是上面的确切示例,其中包含更多框和一个框(框 4),其中包含更多项目……我只将 fo:block font-size 更改为导致分页。