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:
Related
I am transforming an XML into PDF Document using XSLT 1.0 and Groovy. When my table is rendered in PDF, XSLT is inserting Doc Link in every column, whereas my requirement is to render empty white space in the column when no link is found in the XML
<fo:table-cell border="solid 1px black">
<xsl:variable name="link"><xsl:value-of select="link/text()"/</xsl:variable>
<xsl:variable name="space" select="' '"/>
<fo:block>
<xsl:choose>
<xsl:when test="$link">
<fo:basic-link external-destination="url({$link})" color="blue" text-decoration="underline">Doc Link</fo:basic-link></xsl:when>
<xsl:otherwise><xsl:value-of select="$space"/></xsl:otherwise>
</xsl:choose>
</fo:block>
Could it be because of Groovy? If yes how can I fix it?
As #dave pointed out, you've fallen into the trap I described earlier today in Can we have multiple script function in for <xsl:value-of /> element
You need to replace
<xsl:variable name="link"><xsl:value-of select="link/text()"/</xsl:variable>
with
<xsl:variable name="link" select="link/text()"/>
I don't know why this mistake is so common, since the correct code is much shorter and simpler than the incorrect code.
I want to force a line break after a string length of 14 characters in a PDF generated with AH Formatter. So this is my xsl code without any attempt of line breaking:
<xsl:attribute-set name="big" use-attribute-sets="bold">
<xsl:attribute name="font-size">38pt</xsl:attribute>
<xsl:attribute name="line-height">28.84pt</xsl:attribute>
<xsl:attribute name="text-align">center</xsl:attribute>
<xsl:attribute name="letter-spacing">1mm</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="small" use-attribute-sets="bold">
<xsl:attribute name="font-size">27pt</xsl:attribute>
<xsl:attribute name="line-height">27pt</xsl:attribute>
<xsl:attribute name="text-align">center</xsl:attribute>
<xsl:attribute name="letter-spacing">1mm</xsl:attribute>
</xsl:attribute-set>
<xsl:choose>
<xsl:when test="string-length($count_cover)>=14">
<fo:block xsl:use-attribute-sets="small">
<xsl:apply-templates/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block xsl:use-attribute-sets="big">
<xsl:apply-templates/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
Is it possible to force a line break with XSL-FO?
If the title can be converted into string, you can insert <fo:block/> as line break.
<xsl:variable name="cover_title" as="xs:string" select="'Very Long Cover Title! Very Long Cover Title! Very Long Cover Title! '"/>
<xsl:variable name="count_cover" as="xs:integer" select="string-length($cover_title)"/>
<xsl:variable name="lf_position" as="xs:integer" select="14"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$count_cover gt $lf_position">
<fo:block xsl:use-attribute-sets="small">
<xsl:analyze-string select="$cover_title" regex=".{{1}}">
<xsl:matching-substring>
<xsl:value-of select="."/>
<xsl:if test="position() eq $lf_position">
<fo:block/>
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring/>
</xsl:analyze-string>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block xsl:use-attribute-sets="big">
<xsl:value-of select="$cover_title"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The result:
<fo:block font-weight="bold" font-size="27pt" line-height="27pt" text-align="center" letter-spacing="1mm">Very Long Cove<fo:block/>r Title! Very Long Cover Title! Very Long Cover Title! </fo:block>
However this method ignores word boundaries and hyphenation control. If you are intending to make book cover title, it will better to introduce AH Formatter extensions by using fo:block-container.
Use fo:block-container for your title in fixed position and size in the cover page.
Set property #overflow="condense" with #axf:overflow-condense=”font-size".
https://www.antennahouse.com/product/ahf60/docs/ahf-ext.html#axf.overflow-condense
Inside the fo:block-container, place fo:block that stores title contents.
You can get desired result because AH Formatter automatically adjust the font-size according to the content volume.
[Example]
<fo:block-container position="absolute" top="..." left="..." width="..." height="..." overflow="condense" axf:overflow-condense="font-size" font-size="27pt" text-align="center">
<fo:block>
<fo:inline>Very Long Cover Title! Very Long Cover Title! Very Long Cover Title!</fo:inline>
</fo:block>
</fo:block-container>
If you're trying to break words (rather than, e.g., part numbers), then enabling hyphenation may give you a better result than breaking after a fixed number of characters.
You can use linefeed-treatment="preserve" and insert
instead of fo:block, as this answer to Inserting a line break in a PDF generated from XSL FO using <xsl:value-of> notes. Which you can do with <xsl:value-of select="replace(., '(.{14})', '$1
')" />
You can instead insert a zero-width space, , after every 14th character and let AH Formatter break on the zero-width space:
<xsl:template match="text()">
<xsl:value-of
select="replace(replace(., '(\P{Zs}{14})', '$1'),
'(\p{Zs})',
'$1')" />
</xsl:template>`
The inner replace() inserts the character after every 14 non-space characters, and the outer replace() fixes it if the 15th character was a space character.
If you're using a proportional-width font, some sequences of 14 characters (excluding, e.g., 14 constant-width lining numbers) will take more or less width than others, so you might want to insert between more characters so that AH Formatter can do its best to fill the line before breaking.
You can use axf:word-break="break-all" to enable line breaking even inside a word. See https://www.antennahouse.com/product/ahf63/ahf-ext.html#axf.word-break
You can't force a line break in FO, but you can split up the string into separate FO blocks.
<xsl:choose>
<xsl:when test="string-length($count_cover) >= 14">
<fo:block><xsl:value-of select="substring($count_cover, 1, 13)"/></fo:block>
<fo:block><xsl:value-of select="substring($count_cover, 14)"/></fo:block>
</when>
<xsl:otherwise>
<fo:block>
<xsl:value-of select="$count_cover"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
I am using XSL-FO and FOP .95, whenever i write a code in xsl-fo i have to use this statement to generate an empty space:
<fo:block>
<xsl:choose>
<xsl:when test="normalize-space(Seller_Name)!=''">
<xsl:value-of select="normalize-space(Seller_Name)"/>
</xsl:when>
<xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
</xsl:choose>
</fo:block>
I dont want to use these choose when conditions to generate an empty space to save the block collapse. is there any function or property which can be used here? I have tried line-feed-treatment and white-space-collapse but it didnt work. Please advise something.
IF you are happy with what you have above, why not template it. This would reduce the call to three lines:
<xsl:template name="blockwithblank">
<xsl:param name="field"/>
<fo:block>
<xsl:choose>
<xsl:when test="normalize-space($field)!=''">
<xsl:value-of select="$field"/>
</xsl:when>
<xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
</xsl:choose>
</fo:block>
</xsl:template>
That above is once in the whole stylesheet, then each of the calls is only three lines:
<xsl:call-template name="blockwithblank">
<xsl:with-param name="field" select="Seller_Name"/>
</xsl:call-template>
I am not sure you can shorten it more than three lines each call.
Use two templates: one for the regular case and the other, with a higher priority, for the empty case:
<xsl:template match="Seller_Name">
<fo:block>
<xsl:value-of select="normalize-space()"/>
</fo:block>
</xsl:template>
<xsl:template match="Seller_Name[normalize-space() = '']" priority="5">
<fo:block> </fo:block>
</xsl:template>
Your XSLT would need to have a xsl:apply-templates at the appropriate point, but it would make the current template shorter.
If you're doing this a lot with multiple elements, you could match on multiple elements in each of these templates and save a lot of repetition.
Is there any way I can set a fixed height for a block regardless of the content within it? I have a block which sometimes displays some text but sometimes it needs to be empty and keep the same height:
<xsl:choose>
<xsl:when test="$condition">
<fo:block height="30mm">
<xsl:text>TEXTTEXT</xsl:text>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block height="30mm">
<xsl:text> </xsl:text>
</fo:block>
</xsl:otherwise>
</xsl:choose>
The height attribute does not apply to fo:block. To keep a fixed height, wrap the fo:block in a fo:block-container:
<fo:block-container height="30mm">
<fo:block>
<xsl:text> </xsl:text>
</fo:block>
</fo:block-container>
in place of empty text, you may pass .(dot) here. then height will remain and dot will ot displayed.its not proper solution but you may refer it.
I think you are looking for fo:leader, as mentioned in this stackoverflow QA:
XSL-FO - Empty block elements
HTH!
I am using XSL FO to generate a PDF file containing a table with information. One of these columns is a "Description" column. An example of a string that I am populating one of these Description fields with is as follows:
This is an example Description.<br/>List item 1<br/>List item 2<br/>List item 3<br/>List item 4
Inside the table cell that corresponds to this Description, I would like the output to display as such:
This is an example Description.
List item 1
List item 2
List item 3
List item 4
I've learned from searching elsewhere that you can make line breaks in XSL FO using an <fo:block></fo:block> within another <fo:block> element. Therefore, even before I parse the XML with my XSL stylesheet, I replace all occurrences of <br/> with <fo:block/>, so that the literal value of the string now looks like:
This is an example Description.<fo:block/>List item 1<fo:block/>List item 2<fo:block/>List item 3<fo:block/>List item 4
The problem arises when the Description string I am using is obtained using <xsl:value-of>, example as follows:
<fo:block>
<xsl:value-of select="descriptionStr"/>
</fo:block>
In which case, the value that gets output to my PDF document is the literal value, so it looks exactly like the previous example with all the <fo:block/> literals. I've tried manually hard-coding the <fo:block/> in the middle of another string, and it displays correctly. E.g. if I write inside my stylesheet:
<fo:block>Te<fo:block/>st</fo:block>
It will display correctly as:
Te
st
But this does not seem to happen when the <fo:block/> is inside the value of an <xsl:value-of select=""/> statement. I've tried searching for this on SO as well as Google, etc. to no avail. Any advice or help will be greatly appreciated. Thank you!
You could also replace <br/> with
and add a linefeed-treatment="preserve" attribute to your <fo:block>.
Something like:
<fo:block linefeed-treatment="preserve">This is an example Description.
List item 1
List item 2
List item 3
List item 4</fo:block>
Edit
Some users may need to use \n instead of
depending on how they are creating the XML. See Retain the
during xml marshalling for more details.
This helped me and should be simplest solution (working with Apache FOP 1.1):
Why not replace your <br/> with Unicode character called line separator.
<xsl:template match="br">
<xsl:value-of select="'
'"/>
</xsl:template>
See https://en.wikipedia.org/wiki/Newline#Unicode
The following code worked:
<fo:block white-space-collapse="false"
white-space-treatment="preserve"
font-size="0pt" line-height="15px">.</fo:block>
It makes the xsl processor thinks this block contains a line of text, which actually has a 0pt font size.
You can customize line height by providing your own value.
You shouldn't use xsl:value-of instruction but xsl:apply-templates instead: for built-in rule for text node will just output their string value, and for empty br element you could declare a rule matching descriptionStr/br or descriptionStr//br (depending your input) in order to transform to empty fo:block.
Generating strings containing escaped XML markup is seldom the right answer, but if that's what you have to work with, then for input like this:
<Description><![CDATA[This is an example Description.<br/>List item 1<br/>List item 2<br/>List item 3<br/>List item 4]]></Description>
if you're using XSLT 2.0, you can use xsl:analyze-string to get the empty fo:block that you originally wanted:
<xsl:template match="Description">
<fo:block>
<xsl:analyze-string select="." regex="<br/>">
<xsl:matching-substring>
<fo:block />
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</fo:block>
</xsl:template>
but if you are using XSLT 2.0, you can more concisely use linefeed-treatment="preserve" as per #Daniel Haley and use replace() to insert the linefeeds:
<xsl:template match="Description">
<fo:block linefeed-treatment="preserve">
<xsl:value-of select="replace(., '<br/>', '
')" />
</fo:block>
</xsl:template>
If you are using XSLT 1.0, you can recurse your way through the string:
<xsl:template match="Description">
<fo:block linefeed-treatment="preserve">
<xsl:call-template name="replace-br" />
</fo:block>
</xsl:template>
<xsl:template name="replace-br">
<xsl:param name="text" select="." />
<xsl:choose>
<xsl:when test="not(contains($text, '<br/>'))">
<xsl:value-of select="$text" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($text, '<br/>')"/>
<xsl:text>
</xsl:text> <!-- or <fo:block /> -->
<xsl:call-template name="replace-br">
<xsl:with-param name="text" select="substring-after($text, '<br/>')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Try this:
<fo:block><fo:inline color="transparent">x</fo:inline></fo:block>
This code adds a block which contains transparent text, making it look like a new line.
Try using linefeed-treatment="preserve" and \n instead of <br> for a new line.
<fo:block linefeed-treatment="preserve" >
<xsl:value-of select="Description" />
</fo:block>
For XSLT 1.0 I'm using my XSLT Line-Break Template on GitHub.
For XSL-FO it supports
Line breaks
Line delimiters (vs Line breaks)
Series of pointers in a row
Ignore Pointer Repetitions (disable the Series of pointers in a row)
Any string as a pointer to insert a break or a delimiter ("\n" is default)
Line delimiters' height
Default Line delimiter height from a current font size.
Auto ignoring of the "\r" char when searching a break place.
Added support for XSLT 2.0 for a seamless migration.
something else...
For XSLT 2.0 and later consider to use approaches like
XSLT 2.0 xsl:analyze-string (RegEx)
XPath 2.0 tokenize + XSLT (RegEx)
passing sequences as a template parameter (XSLT 2.0)
and so on
I usually use an empty block with a height that can be changed if I need more or less space:
<fo:block padding-top="5mm" />
I know this isn't the best looking solution but it's funtional.
I had a text block that looks like this
<fo:table-cell display-align="right">
<fo:block font-size="40pt" text-align="right">
<xsl:text> Text 1 </xsl:text>
<fo:block> </fo:block>
<xsl:text> Text2 </xsl:text>
<fo:block> </fo:block>
<xsl:text> Text 3</xsl:text>
</fo:block>
NB: note the empty
</fo:block> on it's own is not a direct substitute for <br/> <br/> is an html unpaired abberation that has no direct equivalent in xsl:fo
</fo:block> just means end of block. If you scatter them through your text you wont have valid xml, and your xsl processor will sick up errors.
For the line break formatting you want, each block will occur on a new line. You need a <fo:block> start block and </fo:block> end block pair for each line.