【问题标题】:Inserting a line break in a PDF generated from XSL FO using <xsl:value-of>使用 <xsl:value-of> 在从 XSL FO 生成的 PDF 中插入换行符
【发布时间】:2011-04-09 08:32:51
【问题描述】:

我正在使用 XSL FO 生成一个包含信息表的 PDF 文件。这些列之一是“描述”列。我填充这些描述字段之一的字符串示例如下:

This is an example Description.<br/>List item 1<br/>List item 2<br/>List item 3<br/>List item 4

在与此描述对应的表格单元格内,我希望输出显示如下:

This is an example Description.
List item 1
List item 2
List item 3
List item 4

我从其他地方的搜索中了解到,您可以在 XSL FO 中使用另一个 &lt;fo:block&gt; 元素中的 &lt;fo:block&gt;&lt;/fo:block&gt; 进行换行。因此,甚至在我用我的 XSL 样式表解析 XML 之前,我将所有出现的 &lt;br/&gt; 替换为 &lt;fo:block/&gt;,因此字符串的文字值现在看起来像:

This is an example Description.<fo:block/>List item 1<fo:block/>List item 2<fo:block/>List item 3<fo:block/>List item 4

当我使用的Description字符串是使用&lt;xsl:value-of&gt;获取时出现问题,示例如下:

<fo:block>
    <xsl:value-of select="descriptionStr"/>
</fo:block>

在这种情况下,输出到我的 PDF 文档的值是文字值,因此它看起来与前面的示例完全相同,其中包含所有 &lt;fo:block/&gt; 文字。我尝试在另一个字符串中间手动硬编码&lt;fo:block/&gt;,它显示正确。例如。如果我在样式表中写:

<fo:block>Te<fo:block/>st</fo:block>

它会正确显示为:

Te
st

但是当&lt;fo:block/&gt;&lt;xsl:value-of select=""/&gt; 语句的值内时,这似乎不会发生。我试过在 SO 和 Google 等上搜索这个,但无济于事。任何建议或帮助将不胜感激。谢谢!

【问题讨论】:

    标签: xslt xsl-fo


    【解决方案1】:

    您不应该使用xsl:value-of 指令,而是使用xsl:apply-templates:对于文本节点的内置规则只会输出它们的字符串值,对于空的br 元素,您可以声明匹配descriptionStr/br 的规则或descriptionStr//br(取决于您的输入)以转换为空 fo:block

    【讨论】:

    • 嗨亚历杭德罗,我想我明白你的意思......但就我而言,我很抱歉在我最初的问题中没有说清楚,我实际处理的字符串不是来自 XML 文件本身。它来自我使用 EXSLT str:split 函数 (exslt.org/str/functions/split/str.split.html) 构建的节点集。因此,节点集中的每个子节点都包含在“”元素标记中,因此我无法将模板与包含我的字符串的元素名称匹配。你认为我需要改变我的实现,以便我可以使用 apply-templates 调用吗?非常感谢。
    • @user311811:如果你有&lt;token&gt;This is an example Description.&lt;br/&gt;List item 1&lt;/token&gt;,你可以匹配token/br。但从你的问题来看,这个xsl:value-of select="descriptionStr" 表示descriptionStr 子元素的字符串值。
    • @user311811:您应该能够使用exslt:node-set() 将来自str:split() 的结果视为一个节点集。如果您能够使用 XSLT 2.0,这会容易得多。
    【解决方案2】:

    您还可以将&lt;br/&gt; 替换为&amp;#xA;,并将linefeed-treatment="preserve" 属性添加到您的&lt;fo:block&gt;

    类似:

    <fo:block linefeed-treatment="preserve">This is an example Description.&#xA;List item 1&#xA;List item 2&#xA;List item 3&#xA;List item 4</fo:block>
    

    编辑

    某些用户可能需要使用\n 而不是&amp;#xA;,具体取决于他们创建 XML 的方式。详情请见Retain the &#xA; during xml marshalling

    【讨论】:

    • 我正在尝试同样的事情,但无法正常工作。当我通过 xsd 生成 xml 时,我的输出是 &lt;description&gt;REPAIR CAB DOOR&amp;amp;#xA;REPAIR &amp;amp;#xA;&lt;/description&gt; 从而防止在我的 pdf 文档中添加换行符。你有什么想法
    • @user525146 - 当您说您通过 xsd 生成 xml(xsd 是一种模式)时,我不确定您的意思。您能否创建一个新问题,提供有关如何运行 XSL-FO 的示例和更多详细信息?如果您将问题的链接放在此处的评论中,我会尝试为您查找。
    【解决方案3】:

    &lt;/fo:block&gt; 本身不能直接替代 &lt;br/&gt; &lt;br/&gt; 是 html 未配对缩写,在 xsl:fo 中没有直接等效项

    &lt;/fo:block&gt; 仅表示块结束。如果您将它们分散在您的文本中,您将没有有效的 xml,并且您的 xsl 处理器会出现错误。

    对于您想要的换行格式,每个块将出现在新行上。每行都需要一个&lt;fo:block&gt; 起始块和&lt;/fo:block&gt; 结束块对。

    【讨论】:

    • 在 OP 或任何答案中提到它在哪里只是 &lt;/fo:block&gt; 正在使用或应该使用?
    【解决方案4】:

    试试这个:

    &lt;fo:block&gt;&lt;fo:inline color="transparent"&gt;x&lt;/fo:inline&gt;&lt;/fo:block&gt;

    此代码添加了一个包含透明文本的块,使其看起来像一个新行。

    【讨论】:

    • 我不建议破解,而是建议一个答案。这显然是一个 hack。请参阅@Daniel Haley 以获得正确答案
    • 我没有意识到这里有禁止黑客攻击的规则。
    【解决方案5】:

    以下代码有效:

    <fo:block white-space-collapse="false" 
        white-space-treatment="preserve" 
        font-size="0pt" line-height="15px">.</fo:block>
    

    它使 xsl 处理器认为该块包含一行文本,实际上它的字体大小为 0pt。 您可以通过提供自己的值来自定义行高。

    【讨论】:

      【解决方案6】:

      这对我有帮助,应该是最简单的解决方案(使用 Apache FOP 1.1):

      为什么不用称为 行分隔符的 Unicode 字符替换您的 &lt;br/&gt;

         <xsl:template match="br">
            <xsl:value-of select="'&#x2028;'"/>
         </xsl:template>
      

      https://en.wikipedia.org/wiki/Newline#Unicode

      【讨论】:

      • 仅使用 &amp;#x2028; 对我有用,这是迄今为止最简单的方法,并且不会干扰为提高可读性而插入的换行符。
      【解决方案7】:

      对于 XSLT 1.0,我在 GitHub 上使用我的 XSLT Line-Break Template

      对于 XSL-FO 它支持

      • 换行符
      • 换行符(与换行符相比)
      • 连续的指针系列
      • 忽略指针重复(禁用连续指针系列)
      • 任何字符串作为指针插入分隔符或分隔符(默认为“\n”)
      • 行分隔符的高度
      • 当前字体大小的默认行分隔符高度。
      • 在搜索中断位置时自动忽略“\r”字符。
      • 添加了对 XSLT 2.0 的支持以实现无缝迁移。
      • 别的...

      对于 XSLT 2.0 及更高版本,请考虑使用类似的方法

      • XSLT 2.0 xsl:analyze-string (RegEx)
      • XPath 2.0 标记化 + XSLT (RegEx)
      • 将序列作为模板参数传递 (XSLT 2.0)
      • 等等

      【讨论】:

        【解决方案8】:

        尝试使用 linefeed-treatment="preserve" 和 \n 而不是 &lt;br&gt; 换行。

        <fo:block linefeed-treatment="preserve" >
         <xsl:value-of select="Description" />
        </fo:block>
        

        【讨论】:

          【解决方案9】:

          生成包含转义 XML 标记的字符串很少是正确的答案,但如果这是您必须使用的,那么对于这样的输入:

          <Description><![CDATA[This is an example Description.<br/>List item 1<br/>List item 2<br/>List item 3<br/>List item 4]]></Description>
          

          如果您使用的是 XSLT 2.0,则可以使用 xsl:analyze-string 来获取您最初想要的空 fo:block

          <xsl:template match="Description">
            <fo:block>
              <xsl:analyze-string select="." regex="&lt;br/>">
                <xsl:matching-substring>
                  <fo:block />
                </xsl:matching-substring>
                <xsl:non-matching-substring>
                  <xsl:value-of select="." />
                </xsl:non-matching-substring>
              </xsl:analyze-string>
            </fo:block>
          </xsl:template>
          

          但如果您使用的是 XSLT 2.0,您可以更简洁地使用 @Daniel Haley 的 linefeed-treatment="preserve" 并使用 replace() 插入换行符:

          <xsl:template match="Description">
            <fo:block linefeed-treatment="preserve">
              <xsl:value-of select="replace(., '&lt;br/>', '&#xA;')" />
            </fo:block>
          </xsl:template>
          

          如果您使用的是 XSLT 1.0,则可以通过字符串递归:

          <xsl:template match="Description">
            <fo:block linefeed-treatment="preserve">
              <xsl:call-template name="replace-br" />
            </fo:block>
          </xsl:template>
          
          <xsl:template name="replace-br">
            <xsl:param name="text" select="." />
          
            <xsl:choose>
              <xsl:when test="not(contains($text, '&lt;br/>'))">
                <xsl:value-of select="$text" />
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="substring-before($text, '&lt;br/>')"/>
                <xsl:text>&#xA;</xsl:text> <!-- or <fo:block /> -->
                <xsl:call-template name="replace-br">
                  <xsl:with-param name="text" select="substring-after($text, '&lt;br/>')"/>
                </xsl:call-template>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:template>
          

          【讨论】:

            【解决方案10】:

            如果我需要更多或更少的空间,我通常会使用一个高度可以更改的空块:

            <fo:block padding-top="5mm" />
            

            我知道这不是最好看的解决方案,但它很实用。

            【讨论】:

              【解决方案11】:

              我有一个像这样的文本块

              <fo:table-cell display-align="right">
              <fo:block font-size="40pt" text-align="right">
                  <xsl:text> Text 1 </xsl:text>
                  <fo:block> </fo:block>
                  <xsl:text> Text2 </xsl:text>
                  <fo:block> </fo:block>
                  <xsl:text> Text 3</xsl:text>
              </fo:block>
              

              注意:注意空

              【讨论】:

                猜你喜欢
                • 2017-04-15
                • 2010-11-09
                • 2011-10-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多