When transferring my XML-files to TeX I try to reformat dates – my publisher said I had to use a smaller horizontal space between dates – and can't manage to go beyond the first step.
My input file is this
<a>
<date>January 1900</date>
<date>2. 2. 1902</date>
<date>3. [3]. 1903</date>
<date>[4. 4. 1904]</date>
</a>
where brackets mean that the date is not certain. There are all possible combination of brackets, e.g. second number of the year: 1[9]00. I created a command \mini which makes small space in TeX:
\newcommand{\mini}{\,}
The result after the xslt should be:
January 1900
2.{\mini}2.{\mini}1902
3.{\mini}[3].{\mini}1903
[4.{\mini}4.{\mini}1904]
I wrote a function, which tries to extract the square brackets and store their position to a variable and afterwards concat them back again. But as I don't manage to get the variables to show correct positions, I'm stuck:
<xsl:function name="foo:date-translate">
<xsl:param name="date-string" as="xs:string"/>
<xsl:variable name="opening-square-bracket" as="xs:integer" select="count(substring-before($date-string,'['))"/>
<xsl:variable name="closing-square-bracket" as="xs:integer" select="count(substring-before($date-string,'['))"/>
<xsl:variable name="date-string-without-square-brackets" as="xs:string" select="replace(replace($date-string,'\[',''),'\]','')"/>
<xsl:choose>
<xsl:when test="matches($date-string-without-square-brackets,'\d{1,2}. \d{1,2}. \d{4}')">
<xsl:choose>
<xsl:when test="not(contains($date-string,'['))">
<xsl:value-of select="replace($date-string,'(\d{1,2}). (\d{1,2}). (\d{4})','$1\\mini$2\\mini$3')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring(replace($date-string-without-square-brackets,'(\d{1,2}). (\d{1,2}). (\d{4})','$1\\mini$2\\mini$3'),0,$opening-square-bracket),'[',substring(replace($date-string-without-square-brackets,'(\d{1,2}). (\d{1,2}). (\d{4})','$1\\mini$2\\mini$3'),$opening-square-bracket, $closing-square-bracket))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$date-string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
I finally managed to solve it with recursion. Basically I have the test whether it suits the regex dd. mm. yyyy when the brackets are removed. as this works i can now rebuild the whole string.
<xsl:function name="foo:date-repeat">
<xsl:param name="date-string" as="xs:string"/>
<xsl:param name="amount" as="xs:integer"/>
<xsl:param name="counter" as="xs:integer"/>
<xsl:choose>
<xsl:when test="substring($date-string,$counter,1) =' '">
<xsl:text>\mini</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($date-string,$counter,1)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="$counter <= $amount">
<xsl:value-of select="foo:date-repeat($date-string, $amount,$counter+1)"/>
</xsl:if>
</xsl:function>
<xsl:function name="foo:date-translate">
<xsl:param name="date-string" as="xs:string"/>
<xsl:variable name="date-string-without-square-brackets" as="xs:string" select="replace(replace($date-string,'\[',''),'\]','')"/>
<xsl:choose>
<xsl:when test="matches($date-string-without-square-brackets,'\d{1,2}. \d{1,2}. \d{4}')">
<xsl:choose>
<xsl:when test="not(contains($date-string,'['))"> <!-- Daten ohne eckige Klammer -->
<xsl:value-of select="replace($date-string,'(\d{1,2}). (\d{1,2}). (\d{4})','$1\\mini$2\\mini$3')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="foo:date-repeat($date-string, string-length($date-string),1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$date-string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
I have even expanded my solution, now the function checks tests every space ' ' if before is a dot or a dot and a bracket and afterwards is a number or a bracket and a number:
<xsl:function name="foo:date-repeat">
<xsl:param name="date-string" as="xs:string"/>
<xsl:param name="amount" as="xs:integer"/>
<xsl:param name="counter" as="xs:integer"/>
<xsl:choose>
<xsl:when test="substring($date-string,$counter,1) =' ' and ((substring($date-string,$counter -1,1) = '.' and number(substring($date-string,$counter -2,1)) = number(substring($date-string,$counter -2,1))) or (substring($date-string,$counter -2,2) = '.]' and number(substring($date-string,$counter -3,1)) = number(substring($date-string,$counter -3,1))))">
<xsl:choose>
<xsl:when test="number(substring($date-string,$counter +1,1)) = number(substring($date-string,$counter +1,1))">
<xsl:text>\mini</xsl:text>
</xsl:when>
<xsl:when test="substring($date-string,$counter +1,1) ='[' and number(substring($date-string,$counter +2,1)) = number(substring($date-string,$counter +2,1))">
<xsl:text>\mini</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($date-string,$counter,1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="substring($date-string,$counter,1) ='['">
<xsl:text>{[}</xsl:text>
</xsl:when>
<xsl:when test="substring($date-string,$counter,1) =']'">
<xsl:text>{]}</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($date-string,$counter,1)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="$counter <= $amount">
<xsl:value-of select="foo:date-repeat($date-string, $amount,$counter+1)"/>
</xsl:if>
Related
I have a value <delivery-at>31-Oct-2020 01:03 PM</delivery-at>, instead of the exact time need a three hour time window. For eg need to display value as "31-Oct-2020 01:00 PM - 31-Oct-2020 04:00 PM" in xslt
I could do it using the following
<xsl:template name= "get-expected-eta">
<xsl:variable name= "etadatetime" select= "delivery-at" />
<xsl:if test = "$etadatetime != '' ">
<xsl:variable name= "etadate" select= "substring($etadatetime, 1, 12)" />
<xsl:variable name= "etatime" select= "substring($etadatetime, 13, 15)" />
<xsl:variable name= "etahour" select= "substring($etatime, 1, 2)" />
<xsl:variable name= "etatimecomponent" select= "substring($etatime, 7, 8)" />
<xsl:variable name= "begin-time">
<xsl:choose>
<xsl:when test= "$etatimecomponent='AM'">
<xsl:choose>
<xsl:when test= "$etahour = 12">
<xsl:value-of select= "00"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select= "$etahour"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test= "$etahour = 12">
<xsl:value-of select= "$etahour"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select= "$etahour + 12"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name= "end-time" >
<xsl:choose>
<xsl:when test= "$begin-time + 3 >= 24">
<xsl:value-of select= "($begin-time + 3) - 24"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select= "$begin-time + 3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name= "from-time" select = "concat($etahour, ' ',$etatimecomponent)"/>
<xsl:variable name= "to-time">
<xsl:choose>
<xsl:when test= "number($end-time) >= 12">
<xsl:choose>
<xsl:when test= "$end-time = 12">
<xsl:value-of select= "12"/> PM
</xsl:when>
<xsl:otherwise>
<xsl:value-of select= "$end-time - 12"/> PM
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test= "$end-time = 0">
<xsl:value-of select= "12"/> AM
</xsl:when>
<xsl:otherwise>
<xsl:value-of select= "$end-time"/> AM
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Expected ETA -->
<xsl:value-of select = '$etadate'/>(<xsl:value-of select = '$from-time'/> - <xsl:value-of select = '$to-time'/>)
</xsl:if>
</xsl:template>
I am trying to create PDF output with numbered headings using the DITA OT and a custom plugin. By default, the output contains part numbers, chapter numbers and appendix number in the headings and the TOC, but no numbers in the bookmarks. So far, I have managed to number all the remaining topics in the headings and the TOC, like so (the chapter numbers restart in every part):
bookmap
part I
chapter 1
topic 1.1
topic 1.2
chapter 2
part II
chapter 1
However, I cannot get the same numbers for the bookmarks.
I am using the following code (or override) to select the bookmarks that must be numbered:
<xsl:template match="*[contains(#class, ' topic/topic ')]" mode="bookmark">
<xsl:variable name="mapTopicref" select="key('map-id', #id)[1]"/>
<xsl:variable name="topicTitle">
<xsl:call-template name="getNavTitle"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$mapTopicref[#toc = 'yes' or not(#toc)] or
not($mapTopicref)">
<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:choose>
<xsl:when test="contains($mapTopicref/#class, ' bookmap/part ')">
<xsl:call-template name="getChapterPrefix"/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when test="contains($mapTopicref/#class, ' bookmap/appendix ')">
<xsl:call-template name="getChapterPrefix"/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when test="contains($mapTopicref/#class, ' bookmap/chapter ')">
<xsl:call-template name="getChapterPrefix"/>
<xsl:text> </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:value-of select="normalize-space($topicTitle)"/>
</fo:bookmark-title>
<xsl:apply-templates mode="bookmark"/>
</fo:bookmark>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates mode="bookmark"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I am using the following code to create the numbers (derived from an example in DITA for Print):
<xsl:template name="getChapterPrefix">
<xsl:variable name="topicType">
<xsl:call-template name="determineTopicType"/>
</xsl:variable>
<xsl:variable name="partsCount">
<xsl:value-of select="count($map//*[contains(#class, ' bookmap/part')])"/>
</xsl:variable>
<xsl:variable name="containingChapter" select="ancestor-or-self::*[contains(#class, ' topic/topic')][position()=1]"/>
<xsl:variable name="id" select="$containingChapter/#id"/>
<xsl:variable name="topicChapters">
<xsl:copy-of select="$map//*[contains(#class, ' bookmap/chapter')]"/>
</xsl:variable>
<xsl:variable name="topicAppendices">
<xsl:copy-of select="$map//*[contains(#class, ' bookmap/appendix')]"/>
</xsl:variable>
<xsl:variable name="topicParts">
<xsl:copy-of select="$map//*[contains(#class, ' bookmap/part')]"/>
</xsl:variable>
<xsl:variable name="chapterNumber">
<xsl:choose>
<xsl:when test="$topicChapters/*[#id = $id]">
<xsl:choose>
<xsl:when test="$partsCount=0"> <!-- Bookmaps without parts work fine -->
<xsl:number format="1" value="count($topicChapters/*[#id =$id]/preceding-sibling::*) + 1"/>
</xsl:when>
<xsl:otherwise> <!-- This does not work yet. -->
<xsl:number format="1" value="count($topicChapters/*[#id =$id]/preceding-sibling::*) + 1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$topicAppendices/*[#id = $id]">
<xsl:number format="A" value="count($topicAppendices/*[#id =$id]/preceding-sibling::*) + 1"/>
</xsl:when>
<xsl:when test="$topicParts/*[#id = $id]">
<xsl:number format="I" value="count($topicParts/*[#id =$id]/preceding-sibling::*) + 1"/>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$chapterNumber != ''">
<xsl:value-of select="$chapterNumber"/>
</xsl:when>
</xsl:choose>
</xsl:template>
With this code, parts, appendices and bookmaps without parts are numbered correctly. However, for bookmaps with parts, chapters are numbered consecutively, which is inconsistent.
bookmap
part I
chapter 1
topic 1.1
topic 1.2
chapter 2
part II
chapter 3
Can anybody help me to correct this?
I just found a way to obtain the result I want. The piece of code that calculates the chapter number for bookmaps with parts was modified als follows:
<!-- If there's something in $topicChapters with an id that matches the id of the
context node, then I'm inside a chapter. -->
<xsl:when test="$topicChapters/*[#id = $id]">
<xsl:choose>
<xsl:when test="$partsCount=0"> <!-- Bookmaps without parts -->
<xsl:number format="1" value="count($topicChapters/*[#id =$id]/preceding-sibling::*) + 1"/>
</xsl:when>
<xsl:otherwise> <!-- Bookmaps with parts. -->
<xsl:number format="1" value="count(//*[contains(#class,' bookmap/chapter ')][#id =$id]/preceding-sibling::*)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
This is probably everything but elegant, but then, I'm a tech writer ...
I would like to convert negative and positive decimal into hexadecimal using xslt 1.0.
There's already a topic related to this question here but the answer is given using xslt 2.0.
I tried to reproduce the template using xslt 1.0 but it always returns an empty value.
<xsl:template name="convertDecToHex">
<xsl:param name="pInt" />
<xsl:variable name="vMinusOneHex64"><xsl:number>18446744073709551615</xsl:number></xsl:variable>
<xsl:variable name="vCompl">
<xsl:choose>
<xsl:when test="$pInt > 0">
<xsl:value-of select="$pInt" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$vMinusOneHex64 + $pInt + 1" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="vCompl = 0">
<xsl:text>0</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="vCompl > 16">
<xsl:variable name="result">
<xsl:call-template name="convertDecToHex">
<xsl:with-param name="pInt" select="$vCompl div 16" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($result,substring('0123456789ABCDEF',($vCompl div 16) + 1,1))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('',substring('0123456789ABCDEF',($vCompl div 16) + 1,1))" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Could you help me making it work?
To convert a decimal number to 32-bit signed hexadecimal in pure XSLT 1.0:
<xsl:template name="dec2signedhex">
<xsl:param name="decimal"/>
<xsl:variable name="n">
<xsl:choose>
<xsl:when test="$decimal < 0">
<xsl:value-of select="$decimal + 4294967296"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$decimal"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="q" select="floor($n div 16)"/>
<xsl:if test="$q">
<xsl:call-template name="dec2signedhex">
<xsl:with-param name="decimal" select="$q"/>
</xsl:call-template>
</xsl:if>
<xsl:value-of select="substring('0123456789ABCDEF', $n mod 16 + 1, 1)"/>
</xsl:template>
I have node like:
<item id="37" publish_time="1293829200">
How to convert #publish_time to date like dd.mm.yyyy?
I'm using libxslt
Here is a template that I wrote to convert seconds to a more readable format. You can extend it to cover your needs :
<xsl:template name="convertSecsToTimeStamp">
<xsl:param name="seconds"/>
<xsl:variable name="hours" select="floor($seconds div (60 * 60))"/>
<xsl:variable name="divisor_for_minutes" select="$seconds mod (60 * 60)"/>
<xsl:variable name="minutes" select="floor($divisor_for_minutes div 60)"/>
<xsl:variable name="divisor_for_seconds" select="$divisor_for_minutes mod 60"/>
<xsl:variable name="secs" select="ceiling($divisor_for_seconds)"/>
<xsl:choose>
<xsl:when test="$hours < 10">
<xsl:text>0</xsl:text><xsl:value-of select="$hours"/><xsl:text>hh</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$hours"/><xsl:text>hh</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="$minutes < 10">
<xsl:text>0</xsl:text><xsl:value-of select="$minutes"/><xsl:text>mm</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$minutes"/><xsl:text>mm</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="$secs < 10">
<xsl:text>0</xsl:text><xsl:value-of select="$secs"/><xsl:text>ss</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$secs"/><xsl:text>ss</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Pretty simple question, how can I transform a number (1, 2, 3, etc) into a print friendly ordinal number (1st, 2nd, 3rd, etc) using xslt?
Currently the following works for 1-20 but we may be seeing larger sets of entities getting ranked soon:
<xsl:template name="FormatRanking">
<xsl:param name="Value"></xsl:param>
<xsl:choose>
<xsl:when test="$Value = '1'">
<xsl:value-of select="$Value"/>st
</xsl:when>
<xsl:when test="$Value = '2'">
<xsl:value-of select="$Value"/>nd
</xsl:when>
<xsl:when test="$Value = '3'">
<xsl:value-of select="$Value"/>rd
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Value"/>th
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The only way I would know how to do this would be to change the xsl:when's:
<xsl:when test="$Value = '1'">
<xsl:when test="$Value = '2'">
<xsl:when test="$Value = '3'">
to (not even sure if this is correct):
<xsl:when test="$Value = '1' or $Value = '21' or $Value = '31' ...">
<xsl:when test="$Value = '2' or $Value = '22' or $Value = '33' ...">
<xsl:when test="$Value = '3' or $Value = '22' or $Value = '33' ...">
I'd like to do something similar to this Is there an easy way to create ordinals in C#? but I'm not sure if it's possible in Xslt.
At this point we only need an English solution.
Here's the solution from "Is there an easy way to create ordinals in C#?", translated to XSLT:
<xsl:template name="FormatRanking">
<xsl:param name="Value" select="0" />
<xsl:value-of select="$Value"/>
<!-- a little parameter sanity check (integer > 0) -->
<xsl:if test="
translate($Value, '0123456789', '') = ''
and
$Value > 0
">
<xsl:variable name="mod100" select="$Value mod 100" />
<xsl:variable name="mod10" select="$Value mod 10" />
<xsl:choose>
<xsl:when test="$mod100 = 11 or $mod100 = 12 or $mod100 = 13">th</xsl:when>
<xsl:when test="$mod10 = 1">st</xsl:when>
<xsl:when test="$mod10 = 2">nd</xsl:when>
<xsl:when test="$mod10 = 3">rd</xsl:when>
<xsl:otherwise>th</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
I'm not saying it's a good idea to do this in xslt, but...
<xsl:template name="FormatRanking">
<xsl:param name="Value"></xsl:param>
<xsl:choose>
<xsl:when test="substring($Value,string-length($Value)-1) = '11'">
<xsl:value-of select="$Value"/>th
</xsl:when>
<xsl:when test="substring($Value,string-length($Value)-1) = '12'">
<xsl:value-of select="$Value"/>th
</xsl:when>
<xsl:when test="substring($Value,string-length($Value)-1) = '13'">
<xsl:value-of select="$Value"/>th
</xsl:when>
<xsl:when test="substring($Value,string-length($Value)) = '1'">
<xsl:value-of select="$Value"/>st
</xsl:when>
<xsl:when test="substring($Value,string-length($Value)) = '2'">
<xsl:value-of select="$Value"/>nd
</xsl:when>
<xsl:when test="substring($Value,string-length($Value)) = '3'">
<xsl:value-of select="$Value"/>rd
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Value"/>th
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Edit: or a bit neater solution:
<xsl:template name="FormatRanking">
<xsl:param name="Value"></xsl:param>
<xsl:variable name="penultimateChar" select="substring($Value,string-length($Value)-1, 1)"/>
<xsl:variable name="lastChar" select="substring($Value,string-length($Value))"/>
<xsl:value-of select="$Value"/>
<xsl:choose>
<xsl:when test="$penultimateChar= '1'">
<xsl:text>th </xsl:text>
</xsl:when>
<xsl:when test="$lastChar = '1'">
<xsl:text>st </xsl:text>
</xsl:when>
<xsl:when test="$lastChar = '2'">
<xsl:text>nd </xsl:text>
</xsl:when>
<xsl:when test="$lastChar = '3'">
<xsl:text>rd </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>th </xsl:text>
</xsl:otherwise>
</xsl:choose>
IMO, by using regular expressions (XSLT 2.0), this can be done more concisely:
<xsl:template name="FormatRanking">
<xsl:param name="Value"/>
<xsl:choose>
<xsl:when test="matches(string($Value), '.+[^1][123]$')">
<xsl:value-of select="replace(replace(replace(string($Value), '1$', '1st'), '2$', '2nd'), '3$', '3rd')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(string($Value), 'th')" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This checks for applicability of the special rule before using a regular expression which recognises number strings ending on 1/2/3, excluding 11/12/13. Only then special handling is applied. Otherwise the "th" is just appended.
Please note that the $Value is converted explicitly to a string datatype before applying string operations to avoid potentially funny situations. The return value is anyway implicitly a string.
However, just as a side comment, this only works for the English language. I'd be interested in a true multilingual approach....