xsl:fo inline line wrap and line break

20,636

Solution 1

Linefeeds can be present in an FO document as linefeed characters or numeric character entities (
 or 
); the following FO blocks are completely equivalent:

<fo:block>Before the linefeed&#x000A;after the linefeed.</fo:block>

<fo:block>Before the linefeed
after the linefeed.</fo:block>

The default handling of linefeeds consists in:

  1. converting them into normal spaces;
  2. collapsing them with other adjacent spaces.

This explains why your initial input produces an output where all the content pieces are placed consecutively on the same line.

Using the property linefeed-treatment="preserve" both forms of linefeeds are retained, and the line building process will respect them (they are called forced line-breaks). Note that linefeed-treatment only applies to fo:block elements; it has no effect on fo:inline elements.

Your "edit 1" block has more linefeeds than you need because "... Second Line, Image: ", the image and "some more Text on Line 2 ..." are inside three different fo:block elements, and each block generates its own lines.

Solution 1:

The simplest way to get your desired output is probably this one, with a single fo:block having its linefeed preserved (this is just your original input, after removing the unnecessary fo:inlines and putting everything on the same line:

<fo:block linefeed-treatment="preserve">First Line&#xA;Second Line, Image: <fo:external-graphic scaling="non-uniform" content-height="scale-to-fit" content-width="4mm" height="4mm" src="data:image;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAABfCAIAAAB6Ck5uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA0SURBVHhe7cGBAAAAAMOg+VNf4QBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCoAXMKAAFau+l4AAAAAElFTkSuQmCC"/> some more Text on Line 2&#10;3rd Line</fo:block>

Note that you cannot indent this code, because the extra linefeeds would appear in the output too!

Solution 2:

A second solution, which doesn't involve using the linefeed-treatment property and creates more easily readable code, would be to use empty blocks instead of forced linefeeds:

<fo:block>
    First Line<fo:block/>Second Line, Image: 
    <fo:external-graphic scaling="non-uniform" content-height="scale-to-fit" content-width="4mm" height="4mm" src="data:image;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAABfCAIAAAB6Ck5uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA0SURBVHhe7cGBAAAAAMOg+VNf4QBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCoAXMKAAFau+l4AAAAAElFTkSuQmCC"/> 
    some more Text on Line 2<fo:block/>3rd Line
</fo:block>

As linefeeds are not preserved, we can use them freely:

<fo:block>
    First Line
    <fo:block/>
    Second Line, Image: 
    <fo:external-graphic scaling="non-uniform" content-height="scale-to-fit" content-width="4mm" height="4mm" src="data:image;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAABfCAIAAAB6Ck5uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA0SURBVHhe7cGBAAAAAMOg+VNf4QBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCoAXMKAAFau+l4AAAAAElFTkSuQmCC"/> 
    some more Text on Line 2
    <fo:block/>
    3rd Line
</fo:block>

The empty blocks partition the content of the outer block without creating anything in the output.

Solution 2

Just found the answer in the comments of FOP 1536.

Now I'm using

linefeed-treatment="preserve"

on the block element and I'm replacing all spaces within the text inlines with some unicode magic in the xsl before:

replace(@text, ' ', '&#x00A0;&#x200B;') 

Edit 1: To be a bit more precise, this is the relevant part of the xsl I'm using:

<!DOCTYPE xsl:stylesheet [ 
    <!ENTITY nbsp "&#160;">
    <!ENTITY ZeroWidthSpace "&#x200B;"> 
]>

<fo:block linefeed-treatment="preserve">
    <fo:inline><xsl:value-of select="replace(@text, ' ', '&nbsp;&ZeroWidthSpace;')"/></fo:inline>
</fo:block>

With this approach I'm getting a pretty clean indented xml while having full control over linebreaks and blank spaces.

Share:
20,636
Kai
Author by

Kai

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Updated on July 15, 2022

Comments

  • Kai
    Kai almost 2 years

    I'm struggling with my xsl:fo (Apache Fop 1.1). I'm trying to generate a Block with inline images and inline text elements which might contain newline characters.

    <fo:block>
        <fo:inline>First Line&#10;Second Line, Image: </fo:inline>
        <fo:inline>
            <fo:external-graphic scaling="non-uniform" content-height="scale-to-fit" content-width="4mm" height="4mm" src="data:image;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAABfCAIAAAB6Ck5uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA0SURBVHhe7cGBAAAAAMOg+VNf4QBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCoAXMKAAFau+l4AAAAAElFTkSuQmCC"/>
        </fo:inline>
        <fo:inline> some more Text on Line 2&#10;3rd Line</fo:inline>
    </fo:block>
    

    I expect the output would be

    FirstLine
    SecondLine, Image: || some more Text on Line 2
    3rd Line
    

    what I get is:

    FirstLine SecondLine, Image: || some more Text on Line 2 3rd Line
    

    I've been playing a while now with the following attributes on the fo:block element and/or the fo:inline elements giving strange or unexpected results.

    white-space-collapse="false" 
    white-space-treatment="preserve" 
    linefeed-treatment="preserve"
    

    Any suggestions?

    Edit 1: Changing the inline elements into blocks like this

    <fo:block white-space-treatment="ignore">
        <fo:block white-space="pre">First Line&#xA;Second Line, Image: </fo:block>
        <fo:block>
            <fo:external-graphic scaling="non-uniform" content-height="scale-to-fit" content-width="4mm" height="4mm" src="data:image;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAABfCAIAAAB6Ck5uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA0SURBVHhe7cGBAAAAAMOg+VNf4QBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCoAXMKAAFau+l4AAAAAElFTkSuQmCC"/>
        </fo:block>
        <fo:block white-space="pre"> some more Text on Line 2&#10;3rd Line</fo:block>
    </fo:block>
    

    gives me the expected wrapping within the elements, but a new line is generated for each block which isn't what I'm trying to archive.