换行符 FOP 1.0 上的分页符

Page-break on a line-break FOP 1.0

问题
我正在使用 XSL-T (v1.0) 和 XSL-FO (Apache FOP 1.0) 使用用户在第三方软件中填写的表单中的数据生成 PDF (A4) 报告(输出 XML) .

表单包含用户可以根据需要填写的文本区域(无法控制内容大小)。文本区域内容然后在 XML 中输出为 node/text() (<textArea> ** content: user input ** </textArea>)。为了保留用户输入格式,我在块级使用 linefeed-treatment = "preserve" 属性。

问题是,每当文本区域填充了足够的数据导致分页符时,当分页符发生在换行符时,我有一个 Apache FOP 1.0 nullPointerException。除了使用 keep-together.within-page = "always" 作为解决方法外,我找不到任何解决问题的方法(不是长期解决方案,因为当 text() 长于一页时我会丢失数据。

工具
为了开发 XSL 样式表,我使用:

关于软件使用什么来生成带有 XSL 样式表的 PDF,我无法选择(坚持使用 FOP 1.0 和 XSLT 1.0)。

上下文
为了根据用户在表单中的输入生成报告,该软件允许 link 每种表单的 XSL 样式表。然后它使用 Apache FOP 1.0 和未知的 XSLT 引擎将 XSL 样式表应用到 XML 输出。我负责为每种表单构建那些 XSL 样式表,但我一直被这个问题所困扰。

下面是 FOP 输出的错误和发出错误的 XSL-FO(修剪和完整)。


导致问题的部分 XSL-FO

<fo:block space-before="5mm" text-align="justify">
                <fo:block linefeed-treatment="preserve" keep-together.within-page="auto">
                    <fo:block>
                        <fo:inline/>
                        <fo:inline> TEST
END OF TEST </fo:inline>
                    </fo:block>
                </fo:block>
            </fo:block>

详细错误

Standard Error
        Jun 29, 2018 9:02:59 AM org.apache.fop.cli.Main startFOP
        SEVERE: Exception
        org.apache.fop.apps.FOPException
        java.lang.NullPointerException
            at org.apache.fop.cli.InputHandler.transformTo(InputHandler.java:302)
            at org.apache.fop.cli.InputHandler.renderTo(InputHandler.java:130)
            at org.apache.fop.cli.Main.startFOP(Main.java:174)
            at org.apache.fop.cli.Main.main(Main.java:205)
        Caused by: java.lang.NullPointerException
            at org.apache.fop.layoutmgr.AbstractBreaker.doLayout(AbstractBreaker.java:434)
            at org.apache.fop.layoutmgr.PageBreaker.doLayout(PageBreaker.java:85)
            at org.apache.fop.layoutmgr.PageSequenceLayoutManager.activateLayout(PageSequenceLayoutManager.java:107)
            at org.apache.fop.area.AreaTreeHandler.endPageSequence(AreaTreeHandler.java:238)
            at org.apache.fop.fo.pagination.PageSequence.endOfNode(PageSequence.java:120)
            at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:349)
            at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:177)
            at org.apache.xalan.transformer.TransformerIdentityImpl.endElement(TransformerIdentityImpl.java:1101)
            at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
            at org.apache.xerces.xinclude.XIncludeHandler.endElement(Unknown Source)
            at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source)
            at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
            at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
            at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
            at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
            at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:484)
            at org.apache.fop.cli.InputHandler.transformTo(InputHandler.java:299)
            ... 3 more

        ---------

        java.lang.NullPointerException
            at org.apache.fop.layoutmgr.AbstractBreaker.doLayout(AbstractBreaker.java:434)
            at org.apache.fop.layoutmgr.PageBreaker.doLayout(PageBreaker.java:85)
            at org.apache.fop.layoutmgr.PageSequenceLayoutManager.activateLayout(PageSequenceLayoutManager.java:107)
            at org.apache.fop.area.AreaTreeHandler.endPageSequence(AreaTreeHandler.java:238)
            at org.apache.fop.fo.pagination.PageSequence.endOfNode(PageSequence.java:120)
            at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.endElement(FOTreeBuilder.java:349)
            at org.apache.fop.fo.FOTreeBuilder.endElement(FOTreeBuilder.java:177)
            at org.apache.xalan.transformer.TransformerIdentityImpl.endElement(TransformerIdentityImpl.java:1101)
            at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
            at org.apache.xerces.xinclude.XIncludeHandler.endElement(Unknown Source)
            at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source)
            at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
            at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
            at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
            at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
            at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:484)
            at org.apache.fop.cli.InputHandler.transformTo(InputHandler.java:299)
            at org.apache.fop.cli.InputHandler.renderTo(InputHandler.java:130)
            at org.apache.fop.cli.Main.startFOP(Main.java:174)
            at org.apache.fop.cli.Main.main(Main.java:205)

在 FOP 之前完成 XSL-FO

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <fo:layout-master-set>
        <fo:simple-page-master master-name="coverPage" margin="5mm" page-height="297mm" page-width="210mm" reference-orientation="0" writing-mode="lr-tb">
            <fo:region-body margin-top="25mm" margin-bottom="25mm" margin-left="45mm" margin-right="5mm"/>
            <fo:region-after extent="20mm" precedence="true" display-align="before"/>
            <fo:region-start extent="40mm"/>
        </fo:simple-page-master>
        <fo:simple-page-master master-name="repeatablePage" margin="5mm" page-height="297mm" page-width="210mm" reference-orientation="0" writing-mode="lr-tb">
            <fo:region-body margin-top="30mm" margin-bottom="25mm" margin-left="20mm" margin-right="20mm"/>
            <fo:region-after extent="20mm" precedence="true" display-align="before"/>
        </fo:simple-page-master>
        <fo:page-sequence-master master-name="pageSequence">
            <fo:single-page-master-reference master-reference="coverPage"/>
            <fo:repeatable-page-master-reference master-reference="repeatablePage" maximum-repeats="no-limit"/>
        </fo:page-sequence-master>
    </fo:layout-master-set>
    <fo:page-sequence master-reference="pageSequence" font-family="sans-serif" font-weight="normal">
        <fo:static-content flow-name="xsl-region-after">
            <fo:block text-align="right" font-size="10" color="#083E70">
                Page <fo:page-number/> de <fo:page-number-citation ref-id="endDoc"/></fo:block>
            <fo:block text-align="center" font-size="8" color="#083E70" space-before="8mm">
                <fo:block>ADRESSE LINE 1</fo:block>
                <fo:block>ADRESSE LINE 2</fo:block>
            </fo:block>
        </fo:static-content>
        <fo:static-content flow-name="xsl-region-start">
            <fo:block>
                <fo:external-graphic src="Logo.jpg" content-width="40mm" scaling="uniform"/>
            </fo:block>
            <fo:block-container color="#083E70" border-end-color="#083E70" border-end-width="1pt" border-end-style="solid" block-progression-dimension="235mm">
                <fo:block/>
                <fo:block space-before="10mm" space-after="10mm" font-weight="bold" text-decoration="underline"/>
            </fo:block-container>
        </fo:static-content>
        <fo:flow flow-name="xsl-region-body" font-size="10pt">
            <fo:block text-align="right" space-after="5mm">
                <fo:inline>Bruxelles, le </fo:inline>
                <fo:inline>19/06/2018</fo:inline>.
                    </fo:block>
            <fo:block space-after="10mm" border="0pt solid black" padding="2mm 0">
                <fo:table start-indent="3mm" space-after="10mm" table-layout="fixed" width="147mm">
                    <fo:table-column column-number="1" column-width="35mm"/>
                    <fo:table-column column-number="2" column-width="112mm"/>
                    <fo:table-body>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block>
                                    <fo:inline font-weight="bold">
                                        <fo:inline>Destinataire:</fo:inline>
                                    </fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell display-align="center">
                                <fo:block start-indent="40mm">Mara Kristen<fo:block/>5911 Maple Blvd<fo:block/>1400 NIVELLES<fo:block/>B<fo:block/></fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-body>
                </fo:table>
                <fo:table start-indent="3mm" table-layout="fixed" width="147mm">
                    <fo:table-column column-number="1" column-width="35mm"/>
                    <fo:table-column column-number="2" column-width="112mm"/>
                    <fo:table-body>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block>
                                    <fo:inline font-weight="bold">
                                        <fo:inline>Copie:</fo:inline>
                                    </fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>
                                    <fo:inline>Gold Denis</fo:inline>
                                    <fo:inline font-style="italic"> (Merci Denis !, 3001 HEVERLEE, B)</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block/>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>
                                    <fo:inline>Gold Denis</fo:inline>
                                    <fo:inline font-style="italic"> (Merci Denis !, 3001 HEVERLEE, B)</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block/>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>
                                    <fo:inline>Gold Denis</fo:inline>
                                    <fo:inline font-style="italic"> (Merci Denis !, 3001 HEVERLEE, B)</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-body>
                </fo:table>
            </fo:block>
            <fo:block text-align="left" space-after="10mm"> 
            Ref.: 002956347</fo:block>
            <fo:block space-after="10mm" text-align="center" text-decoration="underline" font-weight="bold">
                <fo:inline>Lettre de consultation - </fo:inline>Psychiatrie</fo:block>
            <fo:block>
                <fo:inline>Concerne: </fo:inline>
            </fo:block>
            <fo:block space-after="10mm" border="1pt solid black" padding="2mm 0">
                <fo:table table-layout="fixed" start-indent="3mm" width="147mm">
                    <fo:table-column column-number="1" column-width="35mm"/>
                    <fo:table-column column-number="2" column-width="112mm"/>
                    <fo:table-body>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block font-weight="bold">
                                    <fo:inline>Nom, Prénom:</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>Quigley, Jules</fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block font-weight="bold">
                                    <fo:inline>Adresse:</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>9816 Main Street,
                                1402 LOOL,
                                B</fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell>
                                <fo:block font-weight="bold">
                                    <fo:inline>Nr. national:</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell>
                                <fo:block>31035622101</fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-body>
                </fo:table>
            </fo:block>
            <fo:block text-align="left">
                <fo:inline font-weight="bold" text-decoration="underline">
                    <fo:inline>Sujet:</fo:inline>
                </fo:inline> <fo:inline>
                    <fo:inline>Exemple</fo:inline>
                </fo:inline>
            </fo:block>
            <fo:block space-before="5mm" text-align="justify">
                <fo:block linefeed-treatment="preserve" keep-together.within-page="auto">
                    <fo:block>
                        <fo:inline/>
                        <fo:inline>TEST
























TEST</fo:inline>
                    </fo:block>
                </fo:block>
            </fo:block>
            <fo:block keep-together.within-page="always">
                <fo:block space-before="10mm" text-align="right">
                    <fo:inline text-decoration="underline">
                        <fo:inline>Signataire</fo:inline>
                    </fo:inline>
                    <fo:block/>
                    <fo:block>
                        <fo:inline>John SMITH</fo:inline>
                    </fo:block>
                    <fo:block>
                        <fo:inline>
                            <fo:inline>Nr. INAMI: </fo:inline>
                        </fo:inline>
                        <fo:inline>45632163140</fo:inline>
                    </fo:block>
                </fo:block>
            </fo:block>
            <fo:block id="endDoc"/>
        </fo:flow>
    </fo:page-sequence>
</fo:root>

编辑
按照@lfurini 的建议,我找到了一种用 &#x00A0;<fo:block/> 替换所有换行符的方法,但它仍然会产生相同的 NPE 错误。
使用的代码如下所示:

 <xsl:template name="gReplaceLineBreak">
        <xsl:param name="pString"/>
        <xsl:choose>
            <xsl:when test="substring-before($pString,'&#xA;')">
                <xsl:value-of select="substring-before($pString,'&#xA;')"/>&#x00A0;<fo:block/>
                <xsl:call-template name="gReplaceLineBreak">
                    <xsl:with-param name="pString" select="substring-after($pString,'&#xA;')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
                </xsl:otherwise>
            </xsl:choose>
    </xsl:template>  

EDIT-2
好的,我终于找到了为什么它不能使用上面的代码。有一个 <fo:inline> 节点包含所有 <fo:block/>。现在我删除了它,它就像@lfurini 所说的那样工作。非常感谢。

这是一个 bug 影响 每个 FOP 版本,包括最新版本。

这是一个(非常丑陋的)解决方法:

  • 删除属性linefeed-treatment="preserve" keep-together.within-page="auto",因为前者会产生错误,而后者会使内容超出页面限制
  • 将该块中的换行符替换为 &#x00A0;<fo:block/>(不间断 space,空块,换行符仅为了可读性)

例如,您示例中的相关块将变为:

            <fo:block space-before="5mm" text-align="justify">
                <fo:block>&#x00A0;<fo:block/>
                    <fo:block>&#x00A0;<fo:block/>
                        <fo:inline/>&#x00A0;<fo:block/>
TEST&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
&#x00A0;<fo:block/>
END OF TEST&#x00A0;<fo:block/>
                    </fo:block>&#x00A0;<fo:block/>
                </fo:block>
            </fo:block>

这适用于从 1.1 到最新的 2.3 的 FOP(无法使用 1.0 进行测试,因为它甚至无法从下载页面获得)。

如何实现: 在您的 XSLT 样式表中,您应该使用特殊的 mode 来处理来自文本区域的用户内容,以及长度为 0 产生 &#x00A0;<fo:block/>.