Inserting a line break in a PDF generated from XSL FO using <xsl:value-of>

一世执手 提交于 2019-11-27 15:27:32

You shouldn't use xsl:value-of instruction but xsl:apply-templates instead: for built-in rule for text node will just output their string value, and for empty br element you could declare a rule matching descriptionStr/br or descriptionStr//br (depending your input) in order to transform to empty fo:block.

Daniel Haley

You could also replace <br/> with &#xA; and add a linefeed-treatment="preserve" attribute to your <fo:block>.

Something like:

<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>

Edit

Some users may need to use \n instead of &#xA; depending on how they are creating the XML. See Retain the &#xA; during xml marshalling for more details.

This helped me and should be simplest solution (working with Apache FOP 1.1):

Why not replace your <br/> with Unicode character called line separator.

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

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

The following code worked:

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

It makes the xsl processor thinks this block contains a line of text, which actually has a 0pt font size. You can customize line height by providing your own value.

Generating strings containing escaped XML markup is seldom the right answer, but if that's what you have to work with, then for input like this:

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

if you're using XSLT 2.0, you can use xsl:analyze-string to get the empty fo:block that you originally wanted:

<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>

but if you are using XSLT 2.0, you can more concisely use linefeed-treatment="preserve" as per @Daniel Haley and use replace() to insert the linefeeds:

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

If you are using XSLT 1.0, you can recurse your way through the string:

<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>

Try this:

<fo:block><fo:inline color="transparent">x</fo:inline></fo:block>

This code adds a block which contains transparent text, making it look like a new line.

For XSLT 1.0 I'm using my XSLT Line-Break Template on GitHub.

For XSL-FO it supports

  • Line breaks
  • Line delimiters (vs Line breaks)
  • Series of pointers in a row
  • Ignore Pointer Repetitions (disable the Series of pointers in a row)
  • Any string as a pointer to insert a break or a delimiter ("\n" is default)
  • Line delimiters' height
  • Default Line delimiter height from a current font size.
  • Auto ignoring of the "\r" char when searching a break place.
  • Added support for XSLT 2.0 for a seamless migration.
  • something else...

For XSLT 2.0 and later consider to use approaches like

  • XSLT 2.0 xsl:analyze-string (RegEx)
  • XPath 2.0 tokenize + XSLT (RegEx)
  • passing sequences as a template parameter (XSLT 2.0)
  • and so on

Try using linefeed-treatment="preserve" and \n instead of <br> for a new line.

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

I usually use an empty block with a height that can be changed if I need more or less space:

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

I know this isn't the best looking solution but it's funtional.

I had a text block that looks like this

<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>

NB: note the empty

</fo:block> on it's own is not a direct substitute for <br/> <br/> is an html unpaired abberation that has no direct equivalent in xsl:fo

</fo:block> just means end of block. If you scatter them through your text you wont have valid xml, and your xsl processor will sick up errors.

For the line break formatting you want, each block will occur on a new line. You need a <fo:block> start block and </fo:block> end block pair for each line.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!