Copy text and replace character in XSL - xslt

I'm transforming a DITA document to a simplified, formatting-based XML to be used as an import into Adobe InDesign. My transformation is going really well, except for one element which omits the text in the output. The element is codeblock. When I don't have a template specifying it at all, the element and any child elements are passed through to the new XML document, but none of the text is passed through. This element should be passed through with text and child elements like every other element in my document for which a specific template is not defined. There's nothing anywhere else in the XSL stylesheet that specifies codeblock or any of its attributes. I am completely stumped and cannot figure out what's going on here.
It is also worth noting that a number of inline elements (cmdname, parmname, userinput, etc.) are converted to bold on output. The downstream XML is for formatting and does not need to know semantic context.
This is what I'm trying to pass through:
<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that <parmname>child elements</parmname> are passed through.</codeblock>
With no template defined for codeblock, this is what I get as a result:
<codeblock><bold/></codeblock>
The actual result I want is:
<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that <bold>child elements</bold> are passed through.</codeblock>
I need the line feeds replaced with character entities because InDesign sees any new line that does not start with an element as a column break. My goal was to simply replace the line feed character with 
 with the following template:
<xsl:template match="codeblock//text()">
<xsl:analyze-string select="." regex="(
)">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
But what I get is:
<codeblock>
<bold/>
</codeblock>
I was finally able to pass the text through using this template:
<xsl:template match="codeblock//text()">
<xsl:copy/>
</xsl:template>
Success! Incidentally, I have to match at any level under codeblock so it includes the text of the child parmname element too. Since I was able to successfully pass it through with <xsl:copy>, I tried this to pass the text through while replacing the line feed at the same time:
<xsl:template match="codeblock//text()">
<xsl:copy>
<xsl:analyze-string select="." regex="(
)">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:copy>
</xsl:template>
But now it won't replace the new line feed. Instead, I get this (which is what I would expect to get without any template defined):
<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that <bold>child elements</bold> are passed through.</codeblock>
I know this is a long and somewhat convoluted question. I just feel like if I could resolve the issue of why it's not passing the text through in the first place, the rest would be fairly straightforward. And I'm sorry, I can't provide my source XML or XSL as it's under NDA, but if you need more, let me know and I'll try to provide it. (My XSL stylesheets are made up of 12 different files, so there's no way for me to provide all of it, even if genericized.)
Any suggestions for what I might look for in my stylesheet that might explain why the text is coming through or any suggestions for how to force it through as I did with <xsl:copy> while still replacing the line feeds will be greatly appreciated!
Edited to add: It has occurred to me that the reason it's not doing the replacement is that it looks like it's not actually a line feed character. It's more like a new line in the code than a line feed character (or hard return) in the text. I think I might need to normalize the text while inserting the 
 character at the end of each line. Still investigating, but suggestions are welcome!
Edited with update: Thanks to the post How to detect line breaks in XSLT, I have gotten closer, but still not quite where I need to be. With this code, I'm able to detect line feeds in the XML and insert the line break character for InDesign:
<xsl:template match="codeblock//text()">
<xsl:for-each select="tokenize(., '\n?')[.]">
<xsl:sequence select="."/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
However, it also inserts the line feed character at the end of the string, even if it's not the end of the line. For instance, I now get:
<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that 
<bold>child elements
</bold> are passed through.
</codeblock>
I don't want the line feed character in front of the 'bold' start and end tags or the codeblock end tag. I just want it to appear where there's an actual new line. I tried replacing \r but that just ignored the new lines and just put it in front of the tags. Does anyone know of another escape character that would work here?

A very long question - yet it's still not clear what exactly you are asking (and no reproducible example, either).
If - as it seems - you want to replace newline characters with the line separator character in all text nodes under the codeblock element, you should be able to do simply:
<xsl:template match="codeblock//text()">
<xsl:value-of select="translate(., '
', '
')" />
</xsl:template>
If this doesn't work, then either you have an overriding template or the text does not contain newline characters. You can test for the first case by changing the template to say:
<xsl:template match="codeblock//text()">BINGO</xsl:template>
and observe the result to see if all targeted text nodes are changed to "BINGO". To test for the second case, you can analyze the text character-by-character using the string-to-codepoints() function.

Your template is missing xsl:non-matching-substring to process the non-matching sections of the text node.
<xsl:template match="codeblock//text()">
<xsl:analyze-string select="." regex="\n">
<xsl:matching-substring>
<xsl:text>
</xsl:text>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
However, michael.hor257k's answer is more simple, as you don't need xsl:analyze-string to just replace a all substrings.

Related

Substring before throwing error

I've the below XML
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p>Industrial drawing: Any creative composition</p>
<p>Industrial drawing: Any creative<fn>
<fnn>4</fnn>
<fnt>
<p>ftn1"</p>
</fnt>
</fn> composition
</p>
</body>
and the below XSL.
<xsl:template match="p">
<xsl:choose>
<xsl:when test="contains(substring-before(./text(),' '),'Article')">
<xsl:text>sect3</xsl:text>
<xsl:value-of select="./text()"/>
</xsl:when>
<xsl:when test="contains(substring-before(./b/text(),' '),'Section')">
<xsl:text> Sect 2</xsl:text>
<xsl:value-of select="./text()"/>
</xsl:when>
<xsl:when test="contains(substring-before(./b/text(),' '),'Chapter')">
<xsl:text> Sect 1</xsl:text>
<xsl:value-of select="./text()"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Here my XSL is working fine for <p>Industrial drawing: Any creative composition</p> but for the below Case
<p>Industrial drawing: Any creative<fn>
<fnn>4</fnn>
<fnt>
<p>ftn1"</p>
</fnt>
</fn> composition
</p>
it is throwing me the below error.
XSLT 2.0 Debugging Error: Error: file:///C:/Users/u0138039/Desktop/Proview/ASAK/DIFC/XSLT/tabel.xslt:38: Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one')
please let me know how can i fix this and grab the text required.
Thanks
The second p element in your example XML has two child text nodes, one containing "Industrial drawing: Any creative" and the other containing a space, "composition", a newline and another six spaces. In XSLT 1.0 it is legal to apply a function that expects a string to an argument that is a set of more than one node, the behaviour is to take the value of the first node and ignore all the others. But in 2.0 it is a type mismatch error to pass two nodes to a function that expected a single value for its parameter.
But in this case I doubt that you really need to use text() at all - if all you care about is seeing whether the string "Article" occurs anywhere within the first word under the p (including when this is nested inside another element) then you can simply use .:
<xsl:when test="contains(substring-before(.,' '),'Article')">
(or better still, use predicates to separate the different conditions into their own templates, with one template matching "Article" paragraphs, another matching "Section" paragraphs, etc.)
The p element in your example has several text nodes, so the expression ./text() creates a sequence. You cannot apply a string function to a sequence; you must convert it to a string first. Instead of:
test="contains(substring-before(./text(),' '),'Article')"
try:
test="contains(substring-before(string-join(text(), ''), ' '), 'Article')"

new line in xsl but the code is not working

In the search result display xsl I added following code but none of them giving me new line.
<xsl:text> &#10; </xsl:text>
<xsl:text>
</xsl:text>
<xsl:text disable-output-escaping="yes" >&#10;</xsl:text>
If you output HTML with your XSLT and want a line break then use a br element e.g. <br/>, see http://www.w3.org/TR/html4/struct/text.html#h-9.3.2.1. The character
is simply treated as white space and does not result in line break unless used inside a pre element or other element with the CSS defining white-space: pre.

How to strip Unicode soft hyphen from PDF bookmarks generated using XSL-FO

I'm converting DITA maps to PDF using the DITA Open Toolkit 1.7 and RenderX XEP. In the DITA topics, product names are inserted using conrefs. One of my product names is quite long. It caused layout problems when used within tables. Therefore I inserted a soft hyphen into the phrase that is reused via conref:
<ph id="PD_FineReader2Comp">DOXiS4 FineReader2­Components</ph>
This works nicely in the generated pages, but creates a problem in the bookmarks where a symbol is displayed in place of the soft hyphen.
Obviously, this is an encoding problem. It seems that UTF-8 characters are properly handled in PDF content, but not in PDF bookmarks where, according to the following sources, some PDF-16 characters can be used (but I did not understand which ones).
http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf
http://www.setasign.de/support/tips-and-tricks/use-unicode-in-string-values/
The DITA Open Toolkit seems to create bookmarks from topic titles using this code fragment:
<fo:bookmark>
<xsl:attribute name="internal-destination">
<xsl:call-template name="generate-toc-id"/>
</xsl:attribute>
<xsl:if test="$bookmarkStyle!='EXPANDED'">
<xsl:attribute name="starting-state">hide</xsl:attribute>
</xsl:if>
<fo:bookmark-title>
<xsl:value-of select="normalize-space($topicTitle)"/>
</fo:bookmark-title>
<xsl:apply-templates mode="bookmark"/>
</fo:bookmark>
The XSL stylesheet has version 2.0.
I would like to create an override that removes the offending character. How can I do this?
Is it possible to properly resolve the encoding problem? (Probably not possible).
Are there any XSL functions or attributes which remove whitespace other than space, tab, linefeed, and carriage return?
Or do I need special handling for the soft hyphen?
Small refinement: If you are using XSLT2, will be more efficient than in this context. In XSLT2 you should always prefer xsl:sequence over xsl:value-of
The simple way to do this is to use the translate() function, which can be used to replace certain characters with other characters, or with nothing. It looks like this is the line that outputs the value you want to fix up:
<xsl:value-of select="normalize-space($topicTitle)"/>
So you could simply modify this to:
<xsl:value-of select="translate(normalize-space($topicTitle), '­', '')"/>
to remove all the soft hyphens. If you would like to replace them with spaces or ordinary hyphens, you could do either of the following, respectively:
<xsl:value-of select="translate(normalize-space($topicTitle), '­', ' ')"/>
<xsl:value-of select="translate(normalize-space($topicTitle), '­', '-')"/>

Extracting string parts from data

Need to extra part of a string. Assume I can access "date" and get on the output
21.01.2013
Now I don't want to have the '.2013'. I tried these lines:
<xsl:value-of select="date"/>
<xsl:variable name="bdate">
<xsl:value-of select="date"/>
</xsl:variable>
<p>Birthday: <xsl:copy-of substring($bdate,1,5) /></p><br/>
The first line only works, with all lines any variations of the last line will always throw an error. But there is a solution for it, I'm sure. Can anyone help for it .. how would last line look like?
<xsl:value-of select="substring($bdate,1,5)" />
Should work (XSLT has to be well formed XML)

xsl strange behaviour using variables

I've stored a file's tree into $onto
<xsl:variable name="onto" select="document('file.xml')"/>
In some places I can use this variable as espected:
<xsl:copy-of select="$onto/rdf:RDF"/>
But I'm having trouble in other places, strange chars are written on output:
<xsl:element name="autor">
<xsl:attribute name="rdf:resource">
<xsl:text>#</xsl:text> <xsl:value-of select="$onto"/>
</xsl:attribute>
</xsl:element>
This is the beginig of the output I've got:
<autor rdf:resource="#
What I'm missing? What's wrong?
If that's to much for an attribute, what can I do?
Thank you
When <xsl:value-of> is applied to a tree fragment, it takes the text content of that tree. In your case, it looks like your XML file doesn't contain any text (other than whitespace) which isn't in an attribute value. I suspect that you mean to select the value of a particular attribute node within the document, e.g.:
<xsl:value-of select="$onto//foo/#bar"/>
(Without knowing the structure of your XML and what you're trying to select, I don't know what the real path would be.)