Force line break after string length - xslt

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>

Related

xsl:choose - Hardcoded 0's being added to end of XSLT string

I am having an issue that I cannot figure out in XLST where there are hardcoded 0's being added to the end of a string that I am not calling for. I am using a choose element to prompt placement of the string or to otherwise pick three 0's.
Can anyone tell in my code what I am doing wrong? See below:
<xsl:for-each select="Export/Record">
<xsl:if test="DebitAmount!=0 and DebitAmount!=''">
<xsl:value-of select="ChargedCorpLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="DepartmentLedgerCode" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryJournalNumber" /><xsl:text>,</xsl:text>
<xsl:value-of select="PFAM" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="LOC" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="ACTV" /><xsl:text> 0000,</xsl:text>
<xsl:value-of select="CLIENT"/><xsl:text> 0000000,</xsl:text>
<xsl:choose>
<xsl:when test="ProjectLedgerCode=null">
<xsl:value-of select="ProjectLedgerCode" /><xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ProjectLedgerCode" /><xsl:text> 000,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="DebitAmount" /><xsl:text>,</xsl:text>
<xsl:value-of select="''" /><xsl:text>,</xsl:text>
<xsl:value-of select="CategoryDesc" /><xsl:text>,</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
my outcome looks like the below where the 000's are adding correctly when the column is blank, but when it is not, it adds the ProjectLedgerCode + 000
This test:
<xsl:when test="ProjectLedgerCode=null">
will return true if the string-value of ProjectLedgerCode is equal to the string-value of a sibling element named null.
If you want to test for ProjectLedgerCode not having a string-value, use:
<xsl:when test="ProjectLedgerCode=''">
or:
<xsl:when test="not(string(ProjectLedgerCode))">
In addition, I believe your results are mixed up.

How to avoid creating duplicated footnotes?

I am parsing an XML that has two footnotes pointing to the same source, as in reference [1] in this example:
This is my parsing code:
<xsl:template match="//sup[#class='reference']/a">
<xsl:variable name="cite_number">
<xsl:value-of select="substring-after(substring-before(./text(),']'),'[')"/> <!-- to remove the [ ] characters -->
</xsl:variable>
<fo:footnote>
<fo:inline font-weight="bold"><fo:inline font-size="6pt" vertical-align="super"><xsl:value-of select="$cite_number"/></fo:inline></fo:inline>
<fo:footnote-body>
<xsl:variable name="cite_id"> <!-- variable to find the content of the cite -->
<xsl:value-of select="substring-after(#href,'#')"/> <!-- to remove the # character -->
</xsl:variable>
<fo:block color="#999999">
<xsl:value-of select="$cite_number"/>
<xsl:text>. </xsl:text>
<xsl:apply-templates select="ancestor::subchapter[#lang]//li[#id=$cite_id]/span[#class='reference-text']"/>
</fo:block>
</fo:footnote-body>
</fo:footnote>
</xsl:template>
The variable cite_number is the number of the cite (1, 2, 3, etc.). If there are two cites pointing at the same source, as shown in the image, the footnote is created twice.
What would be the way of having only one footnote for multiple repeated cites?
I solved it by adding a conditional that prints either the full footnote or just the superindex. In my case, which is MediaWiki pages, I check the id attribute of the parent to determine if there is only one instance of the cite not(contains(../#id,':')) or if there are more than one, and then look for the first one (contains(../#id,'-0')):
<xsl:template match="//sup[#class='reference']/a">
<xsl:variable name="cite_number">
<xsl:value-of select="substring-after(substring-before(./text(),']'),'[')"/> <!-- to remove the [ ] characters -->
</xsl:variable>
<xsl:choose>
<xsl:when test="not(contains(../#id,':')) or (contains(../#id,'-0'))]">
<fo:footnote>
<fo:inline font-weight="bold"><fo:inline font-size="6pt" vertical-align="super"><xsl:value-of select="$cite_number"/></fo:inline></fo:inline>
<fo:footnote-body>
<!-- footnote-body here -->
</fo:footnote-body>
</fo:footnote>
</xsl:when>
<xsl:otherwise>
<fo:inline font-weight="bold"><fo:inline font-size="6pt" vertical-align="super"><xsl:value-of select="$cite_number"/></fo:inline></fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT - How to set correct language in a choos

I'm using XSLT Dita OT to generat PDF files.
For the publicationproces its possible to generate a translation files in EN, FR, DE, IT. Now i used the code as below to manage my output.
I this case the language is a metadata and thats why i use the "# attribute"
But after publishing my code it isn't working. can someone help me with this.
<xsl:choose>
<xsl:when test="#xml:lang = 'EN'">
<fo:block-container xsl:use-attribute-sets="languagecontainer" top="25mm">
<fo:block xsl:use-attribute-sets="languageblock">ENGLISH</fo:block>
</fo:block-container>
</xsl:when>
<xsl:when test="#xml:lang = 'FR'">
<fo:block-container xsl:use-attribute-sets="languagecontainer" top="55mm">
<fo:block xsl:use-attribute-sets="languageblock">FRANÇAIS</fo:block>
</fo:block-container>
</xsl:when>
<xsl:when test="#xml:lang = 'DE'">
<fo:block-container xsl:use-attribute-sets="languagecontainer" top="85mm">
<fo:block xsl:use-attribute-sets="languageblock">DEUTSCH</fo:block>
</fo:block-container>
</xsl:when>
<xsl:when test="#xml:lang = 'IT'">
<fo:block-container xsl:use-attribute-sets="languagecontainer" top="115mm">
<fo:block xsl:use-attribute-sets="languageblock">ITALIANO</fo:block>
</fo:block-container>
</xsl:when>
<xsl:when test="#xml:lang = 'ES'">
<fo:block-container xsl:use-attribute-sets="languagecontainer" top="145mm">
<fo:block xsl:use-attribute-sets="languageblock">ESPAÑOL</fo:block>
</fo:block-container>
</xsl:when>
<xsl:otherwise>
<fo:block>
<xsl:text>no result</xsl:text>
</fo:block>
</xsl:otherwise>
It will be useful to insert debug <xsl:message> instruction before the <xsl:choose> to analyze your problem.
<xsl:message select="'context=',."/>
<xsl:message select="'#xml:lang=',#xml:lang"/>
If the context is not a element, referencing #xml:lang is no meaning. Or if #xml:lang is not the expected one ('FR', 'DE' ,'IT' ,'ES'), you should correct your template to match the actual #xml:lang attribute value.

How to generate empty space in a fo:block if there is no valued in the extracted element?

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.

regex pattern match in XSLT

I've the below XSL template
<xsl:template match="para">
<xsl:analyze-string select="." regex="\d+(?=[-, \d]*$)">
<xsl:matching-substring>
<xsl:variable name="prent">
<xsl:for-each select="document('C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Singapore Precedents of Pleadings\title.xml')/entry/file">
<xsl:value-of select="normalize-space(document(concat('C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Singapore Precedents of Pleadings\',./#name))//chapter/title[//page/#num=regex-group(1)])"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cha">
<xsl:value-of select="substring-before(substring-after($prent,'CHAPTER '),' ')"/>
</xsl:variable>
<xsl:variable name="size">
<xsl:value-of select="string-length($cha)"/>
</xsl:variable>
<xsl:variable name="conct">
<xsl:choose>
<xsl:when test="$size>'1'">
<xsl:value-of select="concat('er:#SPPR_CH_',$cha,'/pg_',regex-group(1))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('er:#SPPR_CH_0',$cha,'/pg_',regex-group(1))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<a href="{$conct}">
<xsl:value-of select="regex-group(1)"/>
</a>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
and below XML
<para>this is 1989 representing text 245</para>
<para>this is sample text 235</para>
<para>this is 234 sample text with comma seperator 345,756</para>
when i run this it is throwing me an error as below.
Invalid regular expression '\d+(?=[-, \d]*$)' - Details: - XTDE1140: The effective value of the 'regex' attribute of the <xsl:analyze-string> instruction must conform to the required syntax for regular expressions
here what i'm trying to achieve is, to get the number at the end of the text, ignoring any other characters, when i tested the regex, it is working fine. and it is here, but when i implement the same in my XSLT, it is throwing me an error.
The expected output is
<a href="245"/>
<a href="235"/>
<a href="345"/>,<a href="756"/>
please let me know how can i fix this.
Thanks