how to control the string value flows in multiple rows in a table. The Value is separated with comma and having dots along with it - xslt-2.0

I am generating a PDFdocument with XSLT. I am having a string value for one of the columns in the table and the value is a combination of dots and hyphen (eg: D1234.23, .745, .258, -98). While displaying the value in the document, the line is ending with comma and dot in one line and the value to the next line.
I tried Keep-together="always" and keep-with-next="always" properties, but it is not working. Can someone please suggest some other option for this.
<fo:table-cell number-columns-spanned="1"
number-rows-spanned="1"
space-before.optimum="8pt"
border-color="black"
border-style="solid"
border-width="0.4pt"
white-space-collapse="false"
white-space-treatment="preserve">
<fo:block space-after.optimum="10pt"></fo:block>
<fo:block-container overflow="condense"
axf:overflow-condense="font-size"
font-weight="bold">
<fo:block text-align="center"
space-before.optimum="2pt"
axf:text-align-string="center"
keep-with-next="always">ABCDEF.TRR, -234, -543, .243, .867, .124, .322, .3123, .1334
</fo:block>
</fo:block-container>
</fo:table-cell>
Actual result:
ABCDEF.TRR, -234, -543, .243, .
867, .124, .322, .3123, .1334
Expected result:
ABCDEF.TRR, -234, -543, .243,
.867, .124, .322, .3123, .1334

you have to add fo:inline for each value separated by ',' as below, it can fix your issue
<fo:block text-align="center" space-before.optimum="2pt" axf:text-align-string="center"
keep-with-next="always">
<fo:inline keep-together.within-line="always">ABCDEF.TRR</fo:inline>,
<fo:inline keep-together.within-line="always">-234</fo:inline>,
<fo:inline keep-together.within-line="always">-543</fo:inline>,
<fo:inline keep-together.within-line="always">.243</fo:inline>,
<fo:inline keep-together.within-line="always">.867</fo:inline>,
<fo:inline keep-together.within-line="always">.124</fo:inline>,
<fo:inline keep-together.within-line="always">.322</fo:inline>,
<fo:inline keep-together.within-line="always">.3123</fo:inline>,
<fo:inline keep-together.within-line="always">.1334</fo:inline>
</fo:block>

Related

straUnexpected output xsl:value-of

Havin a strange issue again. I am tryinodo a simple value- of of field CRETIM:
<CREDAT>20220628</CREDAT>
<CRETIM>112159</CRETIM>
<xsl:attribute name="timestamp">
<xsl:value-of select="EDI_DC40/CRETIM" disable-output-escaping="yes"/>
a
</xsl:attribute>
Output looks as follows:
First6 characterrs are ok, but no idea what happened to th rest.Any idea whats wrong here and how this can be fixed?
Thank you!
If that is XSLT 2 I wonder why you don't use the select attribute on xsl:attribute e.g. <xsl:attribute name="timestamp" select="EDI_DC40/CRETIM"/>.
If you use xsl:value-of wrapped into xsl:attribute make sure you have stray data (like that a letter) inside of xsl:attribute. If the sole a letter is intentional then use e.g. <xsl:attribute name="timestamp" select="concat(EDI_DC40/CRETIM, 'a')"/> or <xsl:attribute name="timestamp"><xsl:value-of select="EDI_DC40/CRETIM"/>a</xsl:attribute>.

Conditional Formatting XSL

I'm trying to make <sup> elements superscripted when I encounter them. I'm iterating over a large file which I can include if required, basically <xml><article><body><p><em></em><sup></sup></p></body></article></xml>
I'm receiving:
Error reported by XML parser: The element type "fo:inline" must be terminated by
the matching end-tag "</fo:inline>"
when trying to use the below to raise the superscripts:
<xsl:for-each select="*">
<fo:block>
<xsl:if test="name() = 'sup'">
<fo:inline vertical-align='super' baseline-shift='4pt'>
</xsl:if>
<xsl:apply-templates select="." mode="xhtml"/>
<xsl:if test="name() = 'sup'">
</fo:inline>
</xsl:if>
</fo:block>
</xsl:for-each>
How can I correct this so the vertical-align='super' is only for sup elements; and is there a better approach to this? I plan to do the same for ems later.
My code which I use currently but puts everything out as plain text is:
<xsl:for-each select="*">
<fo:block><xsl:apply-templates select="." mode="xhtml"/></fo:block>
</xsl:for-each>
If you want to transform <sup></sup> to <fo:inline vertical-align='super' baseline-shift='4pt'></fo:inline> then the usual way with XSLT is to set up a template
<xsl:template match="sup">
<fo:inline vertical-align='super' baseline-shift='4pt'>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
I am not sure whether you want to do that in general or for a particular mode (in that case add mode="mode-name" on the xsl:template and mode="#current" on the xsl:apply-templates).

xslt how to read the document-node()

I have a xml file in which one of the element has the CDATA as the value. I put the CDATA value into a variable which I can see is value type of document-node(1) when i debug my code from oXygen. How do I iterate the document-node()?
copy can give me a new xml file. but what I need is not a new file. I only need to read certain nodes and generate a report based on the values on those nodes. so I directly copy the CDATA to my variable and thought I can manipulate it.
I tried to use substring to read the variable things but failed.
I tried to use document(variable) to open the variable but Oxygen give me the debug-error of FODC0002:I/O error reported by xml parser processing file.
here the file is my variable which looks like a xml file
I did google search for the error but only got bench of non-closed questions like Oxygen throw I/O error when use document().
Would anybody let me know what's going wrong? or give me a better solution?
I also tried parse-xml() but I got the following error from Saxon:
F[Saxon-EE9.5.1.5] the processing instruction target matching "[xX][mM][lL]" is not allowed
F[Saxon-EE9.5.1.5] FODC0006: First argument to parse-xml() is not a well formed and namespace-well-formed XML document.
my code to use parse-xml is as below:
<xsl:template match="data"
<xsl:for-each select="parse-xml(root/outsideData)//nodeLevel1/nodeLevel2">
Could anyone give me a sample about how to use parse-xml()? I did google search but didn't find useful samples.
Thanks very much!
A piece of my data is like the following:
<root>
<outsideData id="123">
<child1 key="124375438"/>
<![CDATA[ <?xml version=1.0 encoding="UTF-8"?><insideData xmlns:xlink="http://www.w3.org/1999/xlink">
<nodeLevel1>
<nodeLevel21>packing</nodeLevel21>
<nodeLevel22 ref="12343-454/560" xlink:href="URN:X-MN:DD%3FM=B888%26SDC=A%26CH=79% .../>
</nodeLevel1>
]]>
</outsideData>
</root>
I want to get the inside CDATA <nodeLevel22> #ref and #xlink which will get DD-FM-B888-26-79
My variables are:
<xsl:for-each select="/root/outsideData">
<xsl:variable name="insideData">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:variable>
<xsl:variable name="Data">
<xsl:value-of
select="normalize-space(substring-after($insideData,'?>'))"
disable-output-escaping="yes"/>
</xsl:variable>
</xsl:foreach>
From the debug I can see that the variable insideData and Data are both value type of document-node(1)
Martin's solution works for me very well :)
But I'm still wondering why the following doesn't work:
<xsl:variable name="insideData">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:variable>
<ref>
<xsl:value-of select="substring-before(substring-after($insideData, '<nodeLevel22 ref'),>/>')"/>
</ref>
Here I got empty <ref/>
If you do <xsl:variable name="varName"><xsl:value-of select="..."/><xsl:variable> then you are creating a temporary document fragment that contains a single text with the string contents of the item(s) selected in the value-of. That does not make sense in most cases, doing <xsl:variable name="varName" select="..."/> is usually sufficient.
As for parsing the contents of the outsideData element with parse-xml, there is indeed not only the escaped XML document inside that element but white space as well, thus if you try to parse the contents as XML you get that error as white space before the XML declaration is not allowed. The whole approach of stuffing the XML into a CDATA section with an element with mixed contents is flawed in my view, if you want to store escaped XML into a CDATA then you should make sure that you use a single element that contains nothing but the CDATA section which then only contains the XML markup with no leading white space.
If you can't change the creation of the input data then you will need to make sure you pass in only that part of the string contents of the element to parse-xml that is a well-formed XML document, so you need some way to strip the white space before the XML declaration doing e.g.
<xsl:for-each select="/root/outsideData">
<xsl:variable name="xml-string" select="replace(., '^\s+', '')"/>
<xsl:variable name="xml-doc" select="parse-xml($xml-string)"/>
<!-- now output data e.g. -->
<xsl:value-of select="$xml-doc//nodeLevel1/nodeLevel22/#ref"/>
...
</xsl:for-each>
Untested but should show the right direction as far as trying to use parse-xml.

apply templates select substring after

I've an XML line like the below.
<title>I. DEFINITION</title>
Here what i'm doing getting the value before '.', this is fine but i want to apply-templates for the content after '.'. i'm unable to know how do i do it. i'm using the below XSLT line.
<xsl:apply-templates select="substring-after(.,'. ')"/>
when i use it, an error is thrown and it is
XSLT 2.0 Debugging Error: Error: file:///C:/Users/u0138039/Desktop/Proview/HK/ArchboldHK2014/XSLT/Chapters.xsl:508: Not a node item - item has type xs:string with value 'DEFINITION' - Details: - XTTE0520: The result of evaluating the 'select' attribute of the <xsl:apply-templates> instruction may only contain nodes
please let me know how i can apply-templates on content after '.'
Thanks.
You can try this template
<xsl:template match="title">
<xsl:copy>
<label><xsl:value-of select="substring-before(., '. ')"/></label>
<caption>
<xsl:variable name="slicetext" select="substring-after(current()/text()[1], '. ')"/>
<xsl:value-of select="$slicetext"/><xsl:apply-templates select="text()[position() > 1]|child::node()[not(self::text())]"/>
</caption>
</xsl:copy>
</xsl:template>
With XSLT 1.0 and 2.0 you can only write and apply-templates for nodes, not for primitive values like strings. I think this changes in XSLT 3.0.
In XSLT 2.0, to process the result of substring-after further, you would need to write a function or a named template taking a string parameter.
If you really want to apply a template you first would need to create a temporary text node with xsl:variable.

XSLT string manipulation

Could someone tell me the easiest way to fix below ? I currently have a file containing a variety of ways to define cross references (basically links to other pages), and I want to convert 2 of them to a single format. Below XML is a simplified sample showing source format :
<Paras>
<Para tag="CorrectTag">
<local xml:lang="en">Look at this section <XRef XRefType="(page xx)">(page 36)</XRef> for more information</local>
</Para>
<Para tag="InCorrectTag">
<local xml:lang="en">Look at some other section (page <XRef XRefType="xx">52</XRef>) for more information</local>
</Para>
</Paras>
What I want to achieve is the following :
<Paras>
<Para tag="CorrectTag">
<local xml:lang="en">Look at this section <XRef XRefType="(page xx)" XRefPage="36"/> for more information</local>
</Para>
<Para tag="InCorrectTag">
<local xml:lang="en">Look at some other section <XRef XRefType="(page xx)" XRefPage="52"/> for more information</local>
</Para>
</Paras>
Using below xslt to transform the [XRef] element
<xsl:template match="XRef">
<xsl:copy>
<xsl:attribute name="XRefType">(page xx)</xsl:attribute>
<xsl:choose>
<xsl:when test="#XRefType='(page xx)'">
<xsl:attribute name="XRefPage" select="substring-before(substring-after(.,'(page '),')')"/>
</xsl:when>
<xsl:when test="#XRefType='xx'">
<xsl:attribute name="XRefPage" select="."/>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
already gives me this output :
<Paras>
<Para tag="CorrectTag">
<local xml:lang="en">Look at this section<XRef XRefType="(page xx)" XRefPage="36"/>for more information</local>
</Para>
<Para tag="InCorrectTag">
<local xml:lang="en">Look at some other section (page<XRef XRefType="(page xx)" XRefPage="52"/>) for more information</local>
</Para>
</Paras>
Which is already solving most of my problem but I'm stuck on how I would get the rest of the [local] element cleaned without removing too much other content.
What I need is something like : if the string "(page " is followed by an XRef element , then remove it. If the string ")" is preceded by an XRef element, remove it. Otherwise, don't touch them.
Any advice on how to tackle this ?
Thanks is advance !
You should be able to tackle that with templates e.g.
<xsl:template match="text()[ends-with(., '(page ')][following-sibling::node()[1][self::XRef]]">
<xsl:value-of select="replace(., '(page $', '')"/>
</xsl:template>
<xsl:template match="text()[starts-with(., ')')][preceding-sibling::node[1][self::XRef]">
<xsl:value-of select="substring(., 2)"/>
</xsl:template>
Of course you need to make sure that any templates for parent element of those text nodes do an apply-templates to process the child nodes.

Resources