I can't find a way to prevent a page break inside a table in the RTF output.
I've tried a lot of combinations of keep-together / keep-with-next but nothing worked for me. The actual version has a parent fo:block with the attribute keep-together.within-page="always" including the whole table.
The problem only occurs when a RTF is generated. The PDF is correct and no page break inside a table exists. The table has a header-row and 3 data-rows. In the RTF there is a page-break after the header-row and the first 2 data-rows. On the next page the header is repeated and the last data-row is generated.
It's very important that the tables don't include a page-break.
Here is the relevant XSLT-Stylesheet code:
<fo:block keep-together.within-page="always" >
<xsl:for-each select="block">
<xsl:call-template name="drawData"></xsl:call-template>
</xsl:for-each>
<fo:table text-align="center">
<xsl:for-each select="row[#type='declare'][1]/column">
<fo:table-column column-number="position()" border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:attribute name="column-width"><xsl:value-of select="#width"/></xsl:attribute>
</fo:table-column>
</xsl:for-each>
<xsl:if test="row[#type='header']">
<fo:table-header>
<fo:table-row keep-together.within-page="2" background-color="#0000FF" color="#FFFFFF">
<xsl:for-each select="row[#type='header'][1]/column/block">
<fo:table-cell border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:attribute name="number-columns-spanned">
<xsl:value-of select="count(../../../row[#type='declare']/column) div count(../../../row[#type='declare'])"/>
</xsl:attribute>
<xsl:call-template name="drawData"></xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</fo:table-header>
</xsl:if>
<fo:table-body>
<xsl:for-each select="row[not(#type='header')]">
<fo:table-row keep-together.within-page="2">
<xsl:for-each select="column/block">
<fo:table-cell border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:call-template name="drawData"></xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
A screenshot of the relevant table:
At the moment (FOP version 2.1), the RTF output has a few limitations compared to the PDF output; in particular, it does not support keep properties.
The linked page states that
RTF output is currently unmaintained
and keeps are
supported by the RTF library but not tied into the RTFHandler
so, while it is probably unlikely that this feature will be fixed in future versions without external help, it could be relatively easy to implement it (in which case it would be a good idea to submit a patch).
Related
I have a requirement that the first row on that page of a table (not the header) is different from the rest. I can't figure out if it can be done with markers or if it's even possible with just FO.
I basically have a legend for a graphic that would be tagged something like:
<row>
<callout>1</callout>
<name>Cog</name>
<description>Super important for stuff</description>
</row>
<row>
<callout>2</callout>
<name>Sprocket</name>
<description>Not nearly as important for stuff</description>
</row>
<row>
<callout>3</callout>
<name>Sprigot</name>
<description>I'm not creative</description>
</row>
<row>
<callout>4</callout>
<name>Last One</name>
<description>Blah blah blah</description>
</row>
The table would output as (X is a count for the number of tables):
Callout
Name
Desc
X - 1
Cog
Super important for stuff
- 2
Sprocket
Not nearly as important for stuff
- 3
Sprigot
I'm not creative
page break
Callout
Name
Desc
X - 4
Last One
Blah blah blah
The count only shows up on the first row on that page, but may obviously show up multiple times if the table spans multiple pages. Is there any way to get it to output like that? I'm using XSL 2.0, Saxon 9, and Apache FOP 2.7.
<xsl:template match="table">
<fo:table>
<fo:table-column column-width="20%"/>
<fo:table-column column-width="30%"/>
<fo:table-column column-width="40%"/>
<fo:table-header>
<fo:table-row>
<fo:table-cell>
<fo:block>Callout</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Name</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Desc</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:apply-templates/>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="row">
<fo:row>
<xsl:apply-templates/>
</fo:row>
</xsl:template>
<xsl:template match="callout">
<fo:table-cell>
<!-- The code needs to go here to get the count to appear only if this row is the first row on the page. -->
<fo:block>
<xsl:text> - </xsl:text>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="name">
<fo:table-cell>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="description">
<fo:table-cell>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
I have to create many <fo:table-row> in a <fo:table-body>. I think it's not nice if I write almost 5 lines of code several times (Maybe 50 times) to create the rows.
Like this:
<fo:table-body>
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:value-of select="row1"/>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:value-of select="row2"/>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:value-of select="row3"/>
</fo:table-cell>
</fo:table-row>
....
</fo:table-body>
I tried to write a function that writes the <fo:table-row> for me. And I have to call the function every time and pass a parameter.
<xsl:function name="fn:createRow">
<xsl:param name="string1"/>
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:value-of select="$string1"/>
</fo:table-cell>
</fo:table-row>
</xsl:function>
And now my XSLT look like this.
<fo:table-body>
<fo:block>
<fo:value-of select="fn:createRow('row1')"/>
<fo:value-of select="fn:createRow('row2')"/>
</fo:block>
</fo:table-body>
But I get the error:
"fo:block" is not a valid child of "fo:table-body"!
But when I work without <fo:block> I get nothing in the PDF:
<fo:table-body>
<fo:value-of select="fn:createRow('row1')"/>
<fo:value-of select="fn:createRow('row2')"/>
</fo:table-body>
Is there any opportunity to do it?
Thanks!
fo:table-cell can contain one or more block-level FOs, including fo:block. (See https://www.w3.org/TR/xsl11/#fo_table-cell)
You don't show your XML, but if all of the row* elements are contained by one element, then in the template for that element, you could do something like:
<fo:table>
<fo:table-body>
<xsl:for-each select="*">
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:block>
<!-- The row* element is the current element here. -->
<xsl:apply-templates />
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:for-each>
</fo:table-body>
</fo:table>
Alternatively, you could make a template for all of the row* elements:
<xsl:template match="*[starts-with(local-name(), 'row')]">
<fo:table-row>
...
(From this distance, it's not clear why the row elements need separate element names.)
When you know the names of the elements that you want to format, you can do:
<xsl:apply-templates select="row1, row2, row3" />
and:
<xsl:template match="row1 | row2 | row3">
<fo:table-row>
...
I think instead of <fo:value-of select="$string1"/> you want <xsl:value-of select="$string1"/>. I would also check whether a fo:table-cell allows inline content, it might be necessary to put a fo:block container in the cell which has the xsl:value-of element as a child.
Also, for the function calls, don't use <fo:value-of select="fn:createRow('row1')"/>, instead, use <xsl:sequence select="fn:createRow('row1')"/>.
Also, fn is a reserved prefix, for your own functions declare and use your own namespace (e.g. xmlns:mf="http://example.com/mf" and <xsl:function name="mf:createRow" ...>...</xsl:function>, then use <xsl:sequence select="mf:createRow('row1')"/>.
So an example of the function would be
<xsl:function name="mf:createRow">
<xsl:param name="input"/>
<fo:table-row>
<fo:table-cell padding-bottom="6pt">
<fo:block>
<xsl:value-of select="$input"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:function>
and you could call it as e.g.
<fo:table-body>
<xsl:sequence select="(1 to 3) ! ('Row ' || . ) ! mf:createRow(.)"/>
</fo:table-body>
I am using below line to achive dynamic row height i.e the height should match with the left column .
But I also need to split the row into different cells ? When I am using simple fo:block-cell attrbutes ,I am not getting dynamic row hight . How can achive both dynamic row hieght and cells ??
<fo:table-row display-align="center">
<xsl:for-each select="xalan:distinct(Number)">
<fo:table-cell block-progression-dimension="auto" >
<fo:block-container height="10mm">
<fo:block font-size="9pt" border-right-width="0.1mm" border-right-style="solid" border-right-color="red" >
<xsl:value-of select="">
<xsl:variable name="">
<xsl:value-of select="">
</xsl:variable>
<xsl:if test="">
<xsl:value-of select=""/>
</xsl:if>
</fo:block>
</fo:block-container>
</fo:table-cell>
snapshot
Update -
I think one way that it could be done is to insert a vertical line after every cell value .Tried this , but somehow vertical line is not printing .
<fo:table-cell number-columns-spanned="2" xsl:use-attribute-sets="myBorder" display-align="center">
<fo:block font-size="10pt" text-align="center">
<fo:table>
<fo:table-body>
<fo:table-row>
<xsl:for-each select="../../../rateDetails[toGeography/sequence = $currentSequence]">
<fo:table-cell><!-- block-progression-dimension="auto" border-right-width="0.1mm" border-right-style="solid" border-right-color="black" text-align="center"> -->
<fo:block-container>
<fo:block font-size="9pt"><!-- border-right-width="0.1mm" border-right-style="solid" border-right-color="black" text-align="center"> -->
<xsl:call-template name="currencySymbol">
<xsl:with-param name="currencyCode" select="$currencyCode" />
</xsl:call-template>
<xsl:value-of select="util:formatCurrency(rate,$language,$countryCode)" />
</fo:block>
</fo:block-container>
<fo:block-container reference-orientation="90">
<fo:block>
<fo:leader leader-pattern="rule" leader-length="100%" rule-style="solid" rule-thickness="0.1mm" color="black"/>
</fo:block>
</fo:block-container>
</fo:table-cell>
</xsl:for-each>
Is there anything that I am missing for vertical line insertion .
snapshot2
If you move the border and padding properties to the fo:table-cell, the border will be the full height of the cell:
<fo:table-cell border-right="0.1mm solid red">
By setting fo:block-container/#height, you're probably finding that the text in a cell can overflow the 10mm but only the 10mm is being used to determine the row height. If you remove the fo:block-container, you should get a variable-height table row.
The new code sample makes things a bit clearer: you're using 2 nested tables.
That's another complication you don't need.
Just use 1 table.
In the first column, place all the geography codes into the first cell. It does not matter how many there are: if you have 1 geography code, the cell will be one line high. If you have 16 geography codes in this cell, the cell will automatically resize to be 3 lines high.
The rest of the row contains cells with the price information. On these cells, define the right border to generate the red vertical line.
<fo:table>
<fo:table-column column-width="..mm" column-number="1"/>
<fo:table-column column-width="..mm" column-number="2"/>
...you'll have to add some code here to add the correct number of columns to your table...
<fo:table-body>
<fo:table-row>
<fo:table-cell>
...place the code to insert the country codes here....
</fo:table-cell>
<xsl:for-each select="../../../rateDetails[toGeography/sequence = $currentSequence]">
<fo:table-cell block-progression-dimension="auto" border-right-width="0.1mm" border-right-style="solid" border-right-color="black" text-align="center">
<fo:block-container>
<fo:block font-size="9pt">
<xsl:call-template name="currencySymbol">
<xsl:with-param name="currencyCode" select="$currencyCode" />
</xsl:call-template>
<xsl:value-of select="util:formatCurrency(rate,$language,$countryCode)" />
</fo:block>
</fo:block-container>
</fo:table-cell>
</xsl:for-each>
I have an XSL-FO stylesheet for a table.
<fo:page-sequence master-reference="defaultPage">
<fo:flow flow-name="xsl-region-body">
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="9pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="150pt"/>
<fo:table-header>
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:text>Column 1</xsl:text></fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block><xsl:text>Column 2</xsl:text></fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block><xsl:text>Column 3</xsl:text></fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<fo:table-row>
<xsl:apply-templates select="rbrOcjena"/>
<xsl:apply-templates select="sifPred"/>
<xsl:apply-templates select="nazPred"/>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
The table can have many rows, so I'd like to break it on new page when it comes to the end of current, when generating PDF. Also, I would like to repeat table header on new page, if that's possible. What attributes should I put in the table tag to make it so?
Thanks!
The table can have many rows, so I'd like to break it on new page when
it comes to the end of current
Without seeing your XSL-FO code, it is difficult to answer this. Please show it. But generally, this is done with keeps and breaks. For example:
<fo:table-row keep-together.within-page="always">
I would like to repeat table header on new page, if that's possible.
What attributes should I put in the table tag to make it so?
Instructing an XSL-FO processor to repeat a number of rows at the top of every page is not done via an attribute to fo:table. Instead, the rows that are to be repeated are put inside fo:table-header:
<fo:table-header>
<fo:table-row>
<fo:table-cell>
<!--Block and text content-->
</fo:table-cell>
</fo:table-row>
</fo:table-header>
Then, the default behaviour of the processor should be to repeat the header rows after a page break. That's because the omit-header-at-break attribute of fo:table is set to "false" by default.
The most obvious reason for this is that it is immediately clear which rows belong to the header and should thus be repeated. If this was just a reference in an attribute of fo:table it would be harder to identify multiple rows as the header. You will find the relevant part of the XSL specification here.
I've faced similar scenario...
Try below code...
<fo:table-row>
<xsl:if test="position() != 1">
<xsl:attribute name="break-before">page</xsl:attribute></i>
.
.
.
I have an HTML table that varies according to its content. 1-3 columns hide. 1 column name changes. All depending on content.
I am creating a PDF version of this HTML table. The PDF version uses apache-FOP with fop v1.0. The PDF output contains 2 of the aforementioned tables on one page. I do not want to create an .xsl for every combination of possibilities. That's a lot of duplication and maintenance.
I can solve the column name change simply by passing the column name in with the XML content. But, conditional visibility of the columns seems to be a far more challenging task.
How can I setup conditional visibility? Is it possible?
I'm having difficulty just getting <fo:table-column visibility="collapse" /> to work. The data still displays with visility set to hidden or collapse. display="none" looked promising. But, the API doesn't show it as a valid property for a table-column.
If I can't conditionally hide a column then I'll need to produce 18 unique xsl files...
Currently, my tables are very basic. I do
<fo:block font-size="10pt">
<fo:table table-layout="fixed" width="100%" border-collapse="separate">
<fo:table-column />
<fo:table-column />
<fo:table-column />
<fo:table-column />
<fo:table-column />
<fo:table-column visibility="collapse" />
<fo:table-column />
<fo:table-column />
<fo:table-column />
<fo:table-column />
<fo:table-header>
<fo:table-cell border-width="0.2mm" border-style="solid">
<fo:block>Header 1</fo:block>
</fo:table-cell>
//...9 other headers (these should show/hide as needed)
</fo:table-header>
<fo:table-body>
<xsl:for-each select="Object/type/SomeItem/ProcedureItemCollection">
<fo:table-row>
<fo:table-cell border-width="0.2mm" border-style="solid" >
<fo:block>
<xsl:value-of select="content1"/>
</fo:block>
</fo:table-cell>
//...9 other cells...
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
XML
<Procedure>
<SiteName>Site1</SiteName>//If Site1, don't create column 2
//If Site2, don't create column 2,3,4
//If Site3, create all columns
<itemtype1>
<item><member1></member1><member2></member2></item>
<item><member1></member1><member2></member2></item>
</itemtype1>
<itemtype2>
<item><member1></member1><member2></member2></item>
<item><member1></member1><member2></member2></item>
</itemtype2>
</Procedure>
Doing it this way, I have little flexibility in creating the table. But, this is all I know how to do.
After a lot of tinkering it turns out that I can add/remove columns using xsl:when and a variable.
First create a variable
<xsl:variable name="SiteName" select="Procedure/SiteName" />
Then conditionally create the 3 elements of the table (column definition, header, body). Starting with the column definition...
<xsl:choose>
<xsl:when test="$SiteName = 'Site1'">
<fo:table-column />//column 2
</xsl:when>
</xsl:choose>
Then the header
<xsl:choose>
<xsl:when test="$SiteName = 'Site1'">
<fo:table-cell border-width="0.2mm" border-style="solid">
<fo:block>Column2</fo:block>
</fo:table-cell>
</xsl:when>
</xsl:choose>
Finally, the body
<xsl:choose>
<xsl:when test="$SiteName = 'Site1'">
<fo:table-cell border-width="0.2mm" border-style="solid" >
<fo:block>
<xsl:value-of select="column2value"/>
</fo:block>
</fo:table-cell>
</xsl:when>
</xsl:choose>