Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xsl:fo inline line wrap and line break

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.

like image 609
Kai Avatar asked Mar 18 '15 09:03

Kai


2 Answers

Linefeeds can be present in an FO document as linefeed characters or numeric character entities (&#x000A; or &#10;); 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.

like image 147
lfurini Avatar answered Nov 10 '22 07:11

lfurini


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.

like image 4
Kai Avatar answered Nov 10 '22 09:11

Kai