XSL-FO Overflow Handling for fo:inline-container Elements - xslt

XSL-FO Overflow Handling for fo:inline-container Elements
My question is: How is it possible to break contents (e.g. fo:block elements) inside a fo:inline-container to a new page if the iherit contents are too long for the current one?
Used Fomatters: AHF 6.2, Apache FOP 2.1
The transformation has to work for both formatters; so a simple solution with fo:float elements is not possible.
Here is a short code extract:
<xsl:template match="myElement">
<fo:block>
<fo:inline-container inline-progression-dimension="33.333%">
<fo:block>
Marginalia Headline
</fo:block>
</fo:inline-container>
<fo:inline-container inline-progression-dimension="66.666%">
<fo:block>
Imagine this is a very long text ...
</fo:block>
<fo:block>
Imagine this is a very long text ...
</fo:block>
<fo:block>
Imagine this is a very long text ...
</fo:block>
<!-- MANY MORE fo:blocks -->
</fo:inline-container>
</fo:block>
</xsl:template>
The thing is, the contents are overflowing the fo:inline-container but are not breaking onto a new page. I think this has something to do with the surrounding fo:block element that keeps everything on a single page.
Any advice would be helpful here. Thank you in advance!

What works
Using fo:list-block
(Ok, you said you'd rather not use this trick ... anyway this works and could be used as a last resort)
You can put the marginalia in the fo:list-item-label and the "normal" text in the fo:list-item-body:
<fo:list-block provisional-distance-between-starts="33.333%">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
Marginalia Headline
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
Lorem ipsum dolor ...
</fo:block>
<!-- other blocks ... -->
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
Using overflowing fo:block-container
Alternatively, you could use a flatter sequence of formatting objects, putting the marginalia into a zero-height block container, so that the following block of normal text will start at the same height:
<fo:block-container height="0pt" overflow="visible" keep-with-next.within-page="always">
<fo:block end-indent="66.666%">
Marginalia Headline
</fo:block>
</fo:block-container>
<fo:block start-indent="33.333%">
Lorem ipsum dolor ...
</fo:block>
<!-- other blocks ... -->
Note that this solution can lead to a marginalia overflowing into the page bottom margin or overlapping the next marginalia if it produces more than X lines, where X is the orphans property of its corresponding normal text (for example, marginalia is three lines long while the normal text has orphans="2").
What does not work
Using fo:float
Even if FOP supports side-floats, I don't think using them would achieve the desired output, as the text would flow around it, returning to use all the available horizontal space as soon as possible:
<fo:block>
<fo:float float="left">
<fo:block width="33.333%" background-color="#AAFFFF">Marginalia Headline</fo:block>
</fo:float>
<fo:block background-color="#FFAAFF">
Lorem ipsum dolor ...
</fo:block>
<!-- other blocks ... -->
</fo:block>
Using fo:inline-container
I think the code in the question does not work as expected not because of something missing in the outer fo:block, but because of something missing in the fo:inline-container containing the long text: the overflow attribute.
If unspecified, its default value is "auto" which means the formatting object processor can do as it likes (probably showing the content even if overflows). With overflow="repeat" the processor should, if needed, create other areas, so that the content will be split into pages:
<fo:block>
<fo:inline-container inline-progression-dimension="33.333%">
<fo:block>
Marginalia Headline
</fo:block>
</fo:inline-container><fo:inline-container inline-progression-dimension="66.666%" overflow="repeat">
<fo:block>
Lorem ipsum dolor ...
</fo:block>
<!-- other blocks ... -->
</fo:inline-container>
</fo:block>
FOP, however, does not support overflow="repeat" (I cannot test with Antenna House XslFormatter, but the conformance page says it is supported).
(Disclosure: I am an inactive FOP developer)

Related

How to display blocks in multiple rows in XSL-FO?

I am trying to add images in multiple rows.
Here is my code:
<fo:block-container reference-orientation="90" >
<xsl:for-each select="Icons/Icon">
<fo:block>
<fo:external-graphic src="{#Source}"/>
</fo:block>
</xsl:for-each>
</fo:block-container>
The <fo:block-container> is placed in an <fo:table-cell>.
You can see examples below where text is other part of the table.
How it looks:
But it should look like this:
I tried to add width for block-container, but it doesn't help.
It can't wrap because you're using a rotated fo:block-container, so what you're seeing is the rotated equivalent of blocks overflowing past the bottom of the page.
It's not clear to me why you're rotating the images, but you could put each graphic inside a separate fo:inline-container and set the reference-orientation on each. (See https://www.w3.org/TR/xsl11/#fo_inline-container)
<fo:table-cell>
<fo:block>
<fo:inline-container reference-orientation="90">
<fo:block>
<fo:external-graphic src="..." />
</fo:block>
</fo:inline-container>
...
</fo:block>
</fo:table-cell>

How to insert a white space between two (inline) elements?

Context
I am creating an XSL-FO document to convert my XML text to PDF.
In the XSL-FO, I have two consecutive inline elements, I would like a white space between them:
<fo:block>
<xsl:number/> <xsl:value-of select="#title"/>
</fo:block>
The expected result would be:
1 Introduction
Instead, I get
1Introduction
It seem XML do not consider this white space.
Attempts
I have tried several possible solutions, without success:
<fo:block>
<xsl:number/><fo:inline white-space="pre"> </fo:inline><xsl:value-of select="#title"/>
</fo:block>
or
<fo:block>
<xsl:number/><fo:inline margin-left="0.5cm"><xsl:value-of select="#title"/></fo:inline>
</fo:block>
None of those ideas produce an acceptable result.
The question:
How to include a white space between two (inline) elements?
Try:
<fo:block>
<xsl:number/>
<xsl:text> </xsl:text>
<xsl:value-of select="#title"/>
</fo:block>
Or:
<fo:block>
<xsl:number/>
<xsl:value-of select="concat(' ', #title)"/>
</fo:block>
The problem with
<fo:inline white-space="pre"> </fo:inline>
is that by default all whitespace-only text nodes within a stylesheet are stripped out, with the exception of those inside xsl:text elements. You can override this with xml:space="preserve"
<fo:inline xml:space="preserve" white-space="pre"> </fo:inline>
All whitespace text nodes that are descendants of an element with this attribute will be kept. Note that unlike normal namespaces you don't need to (and indeed are not allowed to) declare the xml: namespace prefix.
You can also use the following:
&nbsp;

XSL-FO: how to do not begin a chapter in last line

I have a xml document with chapters, and sub-chapters.
I have created an XSL-FO to convert the document to PDF with apache-fop. In the PDF, chapters begin always in a new page using "break-before".
I would like sub-chapters to only start on a page if there is at least 5-10 lines free: sub-chapters do not need to begin on a new page, but it is ugly to have a title in the last line and the first paragraph in the next page.
Any idea how to perform that?
Very simple example of XML file:
<document>
<chapter title="Intro">
<sub-chapter title="any-sub-title">
Any text here
</sub-chapter>
</chapter>
</document>
XSL-FO section:
...
<xsl:for-each select="chapter">
<fo:block font-weight="bold" break-before="odd-page">
<xsl:value-of select="#title"/>
</fo:block>
<xsl:apply-templates/>
</xsl:for-each>
...
<xsl:template match="sub-chapter">
<fo:block font-weight="bold">
<xsl:value-of select="#title"/>
</fo:block>
<xsl:apply-templates/>
</xsl:template>
What I think you are looking for is widow and orphan protection. With widows and orphans, you specify the number of lines in a block that cannot be left alone on one page.
<fo:block widows="4" orphans="4">
your content here.
</fo:block>
You might get a similar behavior with the keep-together or keep-with-next attributes. See the link for a quick how-to.

How come xsl-fo padding is being applied to other blocks?

I'm at a loss trying to understand this. I'm new to using xsl-fo (apache-fop implementation) and I want a block with a border and the content inside padded so it's not bumped up against the border.
However when I add the padding, the padding is also applied to the following block?
<fo:page-sequence master-reference="report-page">
<fo:flow flow-name="xsl-region-body">
<fo:block border="1px solid black" font-size="8pt" margin-bottom="3mm" padding="3mm" >
<fo:block font-weight="bold">FOO</fo:block>
<fo:block>ANOTHER BLOCK</fo:block>
</fo:block>
<fo:block font-size="8pt">BAR</fo:block>
</fo:flow>
Why does BAR become indented by the amount of padding from the previous block? If I remove the padding on the first block, things line up fine?
What you should be doing is setting margin to "0mm" and padding to "3mm" on the block if your intention is to have no space outside the border and a 3mm space between the text and the border.
<fo:block border="1px solid black" font-size="8pt" margin="0mm" padding="3mm">
<fo:block font-weight="bold">FOO</fo:block>
</fo:block>
If the margin-bottom was intended to make space between elements, then you would use space-after or space-before on the following element.
Try to do the following (this is not tested because my Apache FOP installation is not within reach...)
EDIT : Now tested. Padding is only applied to the first block ("FOO").
Specify the padding inside a fo:inline element like this:
<fo:block border="1px solid black" margin-bottom="3mm">
<fo:inline padding="3mm" font-size="8pt" font-weight="bold">
<fo:block>FOO</fo:block>
</fo:inline>
</fo:block>
Also, I've moved the font-weight and font-size properties to the inline element, since the inner block is the only place they are needed (at least in your simple snippet). The outer block only defines the border. Let me know if this works.

XSL:FO Page-Number() function with leading zeros

Lets say I want to print a page number "01" as two separate digits 0 and 1 from 1 to 9
0X-> 01,02,03....
from 10 to 99
10,11,12
<fo:table-cell xsl:use-attribute-sets="TableCellBorder">
<fo:block>
<xsl:value-of select="substring(<fo:page-number/>, 1, 1)"/> //0 or first page-number digit
</fo:block>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="TableCellBorder">
<fo:block>
<xsl:value-of select="substring(<fo:page-number/>, 2, 1)"/>//second page-number digit
</fo:block>
</fo:table-cell>
any ideas how to do this?
<fo:page-sequence master-reference="mymaster" format="01">
...
<fo:page-number/>
...
</fo:page-sequence>
Described here, the only trick is figuring out where to place it.
Sorry, tested with XEP only; no idea if FOP supports it.
UPD. Would you consider adding the words of "leading zeros" in order to make the article searchable better?
This is not possible.
<fo:page-number/> is a XSL-FO construct. It does not get a value until the XSL-FO processor runs. It cannot be manipulated in the XSLT stylesheet that generates the XSL-FO markup.