How to show correctly tabulation in xsl-fo? - xslt

I have an XML document and I am creating an XSL-FO file to convert it to pdf with apache-fop.
In the xml, there are sections <code> to show... code.
In the xsl-fo, I added the white-space="pre" sentence to preserve the code format, but tabulations are shown like single space:
XML section:
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
XSL-FO section:
<xsl:template match="code">
<fo:block font-size="10pt" font-family="monospace" white-space="pre" color="#008000" background-color="#f8f8f8">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
Resulting PDF:
function callback( widget )
{
var ui = widget; // It should be a tab
}
So my question is: How to preserve or set the size of the tabulation?
Edited: I am using apache-fop 1.1
A full example:
proyecto.xml (do not forget to replace the 4 spaces by a tab before "var ui...")
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="proyecto.xsl"?>
<document>
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
</document>
proyecto.xsl
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match ="document">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="OnePage" margin="1in"
page-height="29.7cm"
page-width="21cm"
margin-top="2.5cm"
margin-bottom="2.5cm"
margin-left="3.5cm"
margin-right="2.5cm">
<fo:region-body margin="0cm"/>
</fo:simple-page-master>
<fo:page-sequence-master master-name="Page">
<fo:single-page-master-reference master-reference="OnePage"/>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="Page">
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="code">
<fo:block font-size="10pt" font-family="monospace" white-space="pre">
<xsl:apply-templates/>
<!--<xsl:value-of select="replace( . , 'a', 'b')"/>-->
</fo:block>
</xsl:template>
</xsl:stylesheet>
PDF result (screen):

Replace the tabs by four spaces, as suggested by #mzjn already in the comments.
XML Input
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
XSLT Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="A4-portrait"
page-height="29.7cm" page-width="21.0cm" margin="2cm">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4-portrait">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="10pt" font-family="monospace" linefeed-treatment="preserve" white-space-collapse="false" white-space-treatment="preserve" wrap-option="no-wrap" color="#008000" background-color="#f8f8f8">
<xsl:value-of select="replace(code,' ',' ')"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Now, after replacing the tab characters with whitespaces, the output is rendered correctly by FOP.
There is no obvious flaw with outputting tabs, but bear in mind that Apache FOP is only partially compliant in regard to the whitespace-treatment property: http://xmlgraphics.apache.org/fop/compliance.html , even if it says that only fo:inline elements are affected of this.

Related

How do i create multiple PDF's from a single XML in Apache FOP

I'd like to render multiple reports (PDFs) from a single XML file in Apache FOP.
Each Statement node should create separate PDF's.
I have successfully created a single PDF so far. Below is a sample XML format where each Statement node must be mapped to a separate PDF.
<Statements>
<Statement>
<SummaryDetails>
<SummaryDetail>
</SummaryDetail>
</SummaryDetails>
</Statement>
<Statement>
</Statement>
</Statements>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" exclude-result-prefixes="fo">
<xsl:template match="Statement">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
xmlns:afp="http://xmlgraphics.apache.org/fop/extensions/afp">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin-
top="1.44cm" margin-bottom="1.25cm" margin-right="0.5cm">
<fo:region-body/>
</fo:simple-page-master>
<fo:simple-page-master master-name="A4-first" page-height="29.7cm" page-width="21cm"
margin-top="1.25cm" margin-bottom="1.25cm" margin-left="1.5cm" margin-right="0.5cm">
<fo:region-body region-name="body"/>
</fo:simple-page-master >
</fo:layout-master-set>
<fo:page-sequence master-reference="A4" id="first">
<fo:flow flow-name="xsl-region-body">
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
Any help would be appreciated.

XSL-FO Region-Body background image

I have to add a background image to a page and add some text/content on the page. I am new to XSL-FO so I did some research. It looks like I need to use Region-Body and add the image using background-image attribute.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
<!-- Entry point -->
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
<fo:layout-master-set>
<fo:simple-page-master master-name="cover-page" page-height="21cm" page-width="29.7cm">
<fo:region-body background-image="url('Cover.jpg')" fox:background-image-width="29.7cm" fox:background-image-height="21cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<!-- Cover Page -->
<fo:page-sequence master-reference="cover-page" force-page-count="no-force" format="i">
<fo:flow flow-name="xsl-region-body">
<xsl:call-template name="tpltCoverPage"/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<!-- Cover Page -->
<xsl:template name="tpltCoverPage">
<fo:block></fo:block>
</xsl:template>
</xsl:stylesheet>
The image appears as a background however it is 1700 * 1200 so the image is zoomed in and I can only see partial image in the background. Is there anyway I can zoom out the image so it fits the page height and width (without altering the actual image)?
Using only XSL 1.1 properties, you can position a background image but you can't scale it.
FOP has extension properties for setting the width and height of a background image: https://xmlgraphics.apache.org/fop/2.3/extensions.html#backgroundimages
(AH Formatter can do that and more: https://www.antennahouse.com/product/ahf66/ahf-ext.html#background)
Formatting just the XSL-FO from your sample worked for me with FOP 2.2:
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
<fo:layout-master-set>
<fo:simple-page-master master-name="cover-page"
page-height="21cm" page-width="29.7cm">
<fo:region-body background-image="url('Cover.jpg')"
fox:background-image-width="29.7cm"
fox:background-image-height="21cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<!-- Cover Page -->
<fo:page-sequence master-reference="cover-page"
force-page-count="no-force" format="i">
<fo:flow flow-name="xsl-region-body">
<fo:block></fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>

How to use `xsl:include` with apache fop

I use apache FOP to generate pdf files. I have this xsl code
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<fo:root font-size="11pt" font-family="serif">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4-portrait"
page-height="29.7cm" page-width="21.0cm" margin-top="1cm"
margin-left="1.5cm" margin-right="1cm" margin-bottom="1cm">
<fo:region-body />
<fo:region-after region-name="footer" extent="15mm"/>
</fo:simple-page-master>
<fo:simple-page-master master-name="A4-landscape"
page-width="29.7cm" page-height="21.0cm" margin-top="1cm"
margin-left="1cm" margin-right="1cm" margin-bottom="1cm">
<fo:region-body />
<fo:region-after region-name="footer2" display-align="after" extent="0cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4-portrait">
<xsl:include href="footer_common.xsl"/>
<fo:flow flow-name="xsl-region-body">
....
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Notice the element <xsl:include href="footer_common.xsl"/>
The inclusion does not work!
And here is footer_common.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<fo:static-content flow-name="footer" font-size="7pt">
<fo:table>
<fo:table-column column-width="70mm"/>
<fo:table-column column-width="70mm"/>
<fo:table-column column-width="70mm"/>
<fo:table-body>
...........
</fo:table-body>
</fo:table>
</fo:static-content>
</xsl:stylesheet>
Both .xsl files are in the same resource directory. If it matters - I use eclipse for development. In my java code I get the main .xsl file as a resource stream and use it for the transformation.
The error message is
xsl:include ist an dieser Position in der Formatvorlage nicht zulässig!
which means, xsl:include is not allowed at that position in the template
Can anyone see what I am doing wrong?
Thanks for any help or hints.
xsl:include is a top-level element (actually, a stylesheet declaration), which means that it may only appear as an immediate child of xsl:stylesheet. So, you simply cannot include a stylesheet from within an fo:page-sequence element.
But I think you're not in need of xsl:include and a separate stylesheet, but of xsl:call-template and a separate named template.
Write a separate template similar to the following:
<xsl:template name="footer-ebase">
<fo:static-content flow-name="footer" font-size="7pt">
<fo:table>
<fo:table-column column-width="70mm"/>
<fo:table-column column-width="70mm"/>
<fo:table-column column-width="70mm"/>
<fo:table-body>
<!--...-->
</fo:table-body>
</fo:table>
</fo:static-content>
</xsl:template>
In the main template (the place where you'd like to insert content), reference the named template with:
<fo:page-sequence master-reference="A4-portrait">
<xsl:call-template name="footer-ebase"/>
<fo:flow flow-name="xsl-region-body">
<!--...-->
</fo:flow>
</fo:page-sequence>
Note that it does not always make sense to write named templates. It is advisable if
your code would otherwise be redundant because you need the same functionality in several places
the code would clutter the template and make it hard to read
you use recursion to solve a problem
If you want to split content into separate templates for no apparent reason, then you'd best do away with it alltogether.
You can still put the named template into a separate stylesheet if you wish, but then you need to use xsl:include as a top-level element:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:include href="footer-ebase.xsl"/>
<!--...-->
</xsl:stylesheet>

XSL Second-pass template failing when attribute is selected

I'm wondering if anybody can shed any light on why a particular XSL template is failing to do what is intended. I am generating XSL-FO using XSL 2.0 (Saxon HE). I want to invert certain font styles (so that italic text within an italic paragraph becomes roman, for instance). (In my actual XSL, I do this as the second part of a two-pass process. The issue occurs regardless, so my example shows a single pass for the purposes of simplicity.)
Sample input:
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="skeleton">
<fo:region-body margin="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="skeleton">
<fo:flow flow-name="xsl-region-body">
<fo:block><fo:inline font-style="italic">A Title <fo:inline color="black" font-style="italic">With Some Roman Text</fo:inline> in the Middle</fo:inline></fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
Sample XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//fo:inline[parent::*[#font-style=current()/#font-style]]/#font-style">
<xsl:message>Found nested font-style.</xsl:message>
<xsl:attribute name="font-style">normal</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
My desired result is:
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="skeleton">
<fo:region-body margin="1in" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="skeleton">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:inline font-style="italic">
A Title
<fo:inline color="black" font-style="normal">
With Some Roman Text
</fo:inline>
in the Middle
</fo:inline>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
This is the result I get using this online tester: http://chris.photobooks.com/xml/default.htm
When I run this transform using Saxon the interior fo:inline remains italic and my debug message never appears.
I've found a workaround using for-each (if I do not attempt to select the attribute, but rather select the matching fo:inline, the template is triggered). But I'm curious to know what is wrong with this, in my opinion, much cleaner solution.
Thanks!
(Just to add a little more information, here's the less-ideal template that does get me the result I want, at least so far:
<xsl:template match="//fo:inline[parent::*[#font-style=(current()/#font-style)]]">
<xsl:message>Found nested font-style.</xsl:message>
<xsl:copy>
<xsl:for-each select="#*">
<xsl:message><xsl:value-of select="local-name()"/></xsl:message>
<xsl:choose>
<xsl:when test="local-name()='font-style'">
<xsl:attribute name="font-style">normal</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</xsl:copy>
It is possibly something to do with this line...
<xsl:template match="//fo:inline[parent::*[#font-style=current()/#font-style]]/#font-style">
In XSLT 1.0, I get an error...
The current() function may not be used in a match pattern.
I am not sure if the same happens in XSLT 2.0, but try changing the line to this, and see if that makes a difference
<xsl:template match="//fo:inline[#font-style = parent::*/#font-style]/#font-style">
I managed to obtain the result you are looking for changing the XPath expression to
//fo:inline[parent::*[#font-style]]/#font-style
The previous XPath expression matched no nodes in the document, so the template was never called (and so the message was also not printed).
Taking this to its logical extension, you probably want to convert to "normal" any fo:inline that has an odd number of ancestor fo:inline elements with the same font-style as this one (e.g. if you had italic within italic within italic then only the middle one of the three would want to be converted back to normal).
<xsl:template match="fo:inline[
count(ancestor::fo:inline[#font-style = current()/#font-style]) mod 2 = 1]">
This works because XSLT 2.0 defines current() in a pattern as the node that the pattern is being tested against (in this case the fo:inline that is the subject of the outer predicate).

Aligning an element to the right using XSL-FO, Apache FOP

I'm using XSL-FO with Apache FOP to take a sexy looking XML file and output it as a PDF, however I'm having a really basic problem trying to get a particular bit of information (the address) to be positioned from the right of the page, I can force it over to the right by increasing the left attribute, but if I change my page size, orientation or margins this will immediately be useless.
Below is the code for the XSL, note the comment on line 23.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="all-pages"
page-height="297mm"
page-width="210mm"
margin-top="1cm"
margin-bottom="1cm"
margin-left="1cm"
margin-right="1cm" >
<fo:region-body margin-top="5cm" margin-bottom="1.1cm"/>
<fo:region-before extent="1cm"/>
<fo:region-after extent="5mm"/>
</fo:simple-page-master>
<fo:page-sequence-master master-name="default-sequence">
<fo:repeatable-page-master-reference master-reference="all-pages"/>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="default-sequence">
<fo:static-content flow-name="xsl-region-before" font-size="10pt" font-family="Helvetica" >
<!-- HERE'S MY PROBLEM, THE RIGHT ATTRIBUTE ISN'T BEHAVING ITSELF -->
<fo:block-container absolute-position="absolute" right="4cm" top="1cm" width="6cm" border-style="solid" border-width="1mm" >
<fo:list-block >
<fo:list-item>
<fo:list-item-label>
<fo:block></fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block>ABC</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item>
<fo:list-item-label>
<fo:block></fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block>123</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block-container>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after" padding-top="2pt" border-top-style="solid" border-top-width="1pt" border-top-color="rgb(192,192,192)" font-size="10pt" font-family="Helvetica">
<fo:block></fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" font-size="10pt" font-family="Helvetica">
<fo:block></fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
And as you can see by this screenshot the element isn't positioning correctly:
Any one know know why this is happening?
Bugger, looked at the FOP changelog and the two years between versions 0.95 and 1.0 made me think whether it was a bug, downloaded the new binaries and it's now positioning my element from the right.