XSLT 2.0 co-ordinating multiple modes - xslt

XSLT 2.0, stylesheet and data at https://xsltfiddle.liberty-development.net/bFDb2D3/4
I am transforming medieval documents encoded in tei-xml into webpages where the user can toggle between two different views of the documents, as well as see the translation and various footnotes (eg). This requires multiple layers of processing to output:
Two latin versions ('inter' and 'diplo') between which the user can toggle (derived from the same tei markup)
Translated version with almost no transformations (just paragraph formatting and italics)
Critical apparatus using footnoting # a, b, c, etc.
Historical footnotes using footnoting # 1, 2, 3, etc.
I am using modes in order to handle the levels of processing, and each mode on its own works fine, but together they are missing outputs.
What should output:
<div class="inter"><p> with all transformations mode inter + fn-add-marker
[this should contain <a href>, superscript letters and numbers in text]
<div class="diplo"><p> with all transformations mode diplo + fn-add-marker
[this should contain [text] , line numbers, superscript letters and numbers in text]
<div><p> with translations
<div> with critical apparatus
<div> with footnotes
The XSLTfiddle output is:
URL and superscript letters ok! missing superscript numbers (mode fn-add-marker)
Superscript letters ok! Line # and [text] ok except where inside <persName> or <placeName> (ie.<xsl:template match="tei:lb"> <xsl:template match="tei:supplied">) and missing superscript numbers (mode fn-add-marker)
ok!
ok!
ok!
With respect to #2, the missing line # and [text] appear to be a result of the templates that treat <persName> and <placeName> not handing off to other templates? (templates at lines 173-218)
All templates concerning mode fn-add-marker are at lines 41-77.
Many thanks in advance.

Basically in XSLT 2, once you work with named modes, you need to make sure in a template belonging to a certain mode, having e.g. mode="foo", that you use e.g. mode="foo" or more general mode="#current" on any xsl:apply-templates inside to ensure processing continues in that mode. See https://www.w3.org/TR/xslt20/#element-apply-templates for details.
At https://xsltfiddle.liberty-development.net/gWmuiK7 I have tried XSLT to fix your stylesheet and then at https://xsltfiddle.liberty-development.net/bFDb2D3/5 you can see the result of applying the fixed stylesheet. Not sure whether that programmatic approach is the right tool but it might help to demonstrate the suggested use mode mode on xsl:apply-templates.
Then I think you need to make sure you process the added markers in the two new modes:
<!-- adds fn numbers -->
<xsl:template match="tei:date[#type='deposition_date']" mode="inter dilpo">
<xsl:apply-templates mode="#current"/>
<xsl:apply-templates select="." mode="number"/>
</xsl:template>
<xsl:template match="tei:note[#type='public'] | tei:fn-marker" mode="inter diplo">
<xsl:apply-templates select="." mode="number"/>
</xsl:template>
<xsl:template match="tei:date[#type='deposition_date'] | tei:note[#type='public'] | tei:fn-marker" mode="number">
<sup>
<xsl:number count="tei:date[#type='deposition_date'] | tei:note[#type='public'] | tei:fn-marker" format="1" level="any"/>
</sup>
</xsl:template>
<!-- end of footnote transformations -->
https://xsltfiddle.liberty-development.net/bFDb2D3/6 lines 51 to 66.

Related

XSLT 1.0 - change the order of lines

I want to change the order of line cac:InvoiceLine depending on this node:
cac:AdditionalItemProperty/cbc:Value
All InvoiceLines that have Item type=RC must be gruop at the end of lines, and all that have CU must be on the top.
If the mentioned values are the only ones you are concerned about, then it seems like you could just sort alphabetically by that value; see xsl:sort. You could just put this inside the xsl:for-each or xsl:apply-templates where you process your invoice lines:
<xsl:sort select="cac:AdditionalItemProperty/cbc:Value" />
On the other hand, if you only want to output only the line items with the mentioned values, you could select them separately. For example, assuming you have a template which matches your invoice lines, you'd first apply it to the 'CU' ones and then to the 'RC' ones:
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='CU']" />
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='RC']" />

Is there a way to add a straight line between two flow section in renderX (XSLT)

I'm using the rx:flow-section functionnaly of RenderX to separate a page in two. However I would like to have a straight line that would go all the way through the page between the two flow section.
Is this something possible ? I have a limited knowledge, and I assume it would not be possible to add a fo:leader because if I had one it will be duplicated on each side of the flow-section.
Well, RenderX XEP does not provide a special extension to draw the gutter/column rule. However, it can be done. one way that is likely not desired would be to format all other content with a background-color of white and insert a full-page length rule or set of rules.
I will give this solution. It will scare off most because it is only internally probably doing what you could do. But is also shows just a small sample of what you can do.
I have done this in the past by taking advantage of RenderX's Intermediate Output format (XEPOUT) and a few tricks. When you are using RenderX, you can request to output XEPOUT instead of the final output format (like PDF). XEPOUT is a structured and documented XML format. You can use XSL to modify it and then send that modified XEPOUT back to the engine to get the final PDF.
Essentially the process would be:
XML + XSL -> XEPOUT + XSL -> new XEPOUT -> RenderX -> PDF
Just adding that one step in the process to use an XSL to modify XEPOUT. I will post this below, if you need more information on how to make this work in your environment, it would highly depend on how you use or integrate RenderX.
There are many tricks one can implement. In this case, what I did is apply a red background color behind the rx:flow-section. If you formatted that to the XEPOUT you would find in the content (among all the other text and stuff):
<xep:rgb-color red="1.0" green="0.0" blue="0.0"/>
<xep:rectangle x-from="72000" y-from="93600" x-till="282000" y-till="676800"/>
This is the red rectangle behind each of the columns of the flow-section.
If I were to format that document I would get this:
But if I instead format to XEPOUT and then use XSL to process it, I can change the document before creating the PDF.
Using a simple XSL, I could actually remove those red rectangles and use the dimensions and make a line between the columns. This example assumed only a two column document but you could modify to be as you wish (including picking an alternate color than red). I didn't do the full job here, you could enhance this to more center the line, or even implement multiple lines. This is only an example to get you rolling should you choose to do something like this.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
xmlns:xep="http://www.renderx.com/XEP/xep" exclude-result-prefixes="math" version="1.0">
<xsl:template match="xep:page">
<!-- Get page width -->
<xsl:variable name="page-width">
<xsl:value-of select="number(#width)"/>
</xsl:variable>
<!-- get lower and upper y-pos of longest line {color} xep:rectangle -->
<xsl:variable name="y-till-pos">
<xsl:value-of select="math:min(xep:rgb-color[#red='1.0']/following-sibling::xep:rectangle[1]/#y-till)"/>
</xsl:variable>
<xsl:variable name="y-from-pos">
<xsl:value-of select="xep:rgb-color[#red='1.0']/following-sibling::xep:rectangle[1]/#y-from - 12000"/>
</xsl:variable>
<xep:page>
<xsl:apply-templates select="#*"/>
<!-- Draw Line -->
<xep:line x-from="{$page-width div 2 - 500}" y-from="{$y-from-pos}" x-till="{$page-width div 2 + 500}" y-till="{$y-till-pos}" thickness="1000" style="solid"/>
<xsl:apply-templates select="*"/>
</xep:page>
</xsl:template>
<!-- remove red and rectangle -->
<xsl:template match="xep:rectangle[preceding-sibling::*[1][name()='xep:rgb-color'][#red='1.0']]"/>
<xsl:template match="xep:rgb-color[#red='1.0']"/>
<!-- identity copy rules -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The result in two-page view in PDF shows the result:
Which is a rx:flow-section with a column divider like you wish.
As I said, it is a lot but there is so much more you can do using the same techniques. Like bookfolding results or making n-up pages or applying gradients to text or injecting page count marks or barcodes.

Displaying language labels in sidebar facets

I asked this question a few days ago regarding on how to display the language labels in item view instead of its ISO version. How to apply these in sidebar facets and its search filters? I can't find the xslt for generating the displayed values in the sidebar facets.
In the image above, I would like to change the displayed values into:
English (5325)
French (432)
Chinese (8)
Khmer (5)
Japanese (2)
And then when clicking for example the sidebar facet for French, the displayed value in the search filter should be Language Equals French and not like the image below (Language Equals fr):
Thanks in advance.
What I have tried
I don't know if this is the most efficient way to do this so any advice would be appreciated.
<xsl:template
match="dri:list[#n='language']/dri:item/dri:xref/text()
| dri:list[#id='aspect.discovery.SidebarFacetsTransformer.list.language']/dri:item/text()
| dri:div[#id='aspect.discovery.SearchFacetFilter.div.browse-by-language-results']/dri:table/dri:row/dri:cell/dri:xref/text()">
<xsl:apply-templates select="*[not(name()='head')]"/>
<xsl:variable name="language">
<xsl:value-of select="substring-before(.,' (')"/>
</xsl:variable>
<xsl:for-each select=".">
<xsl:value-of select="concat(util:isoLanguageToDisplay($language),' (', substring-after(.,'('))"/>
</xsl:for-each>
</xsl:template>
The language labels of the dc.language.iso in my sidebar facets is now displaying properly both in the home page and search results.
When clicking View More:
My only problem now is this:
I don't know how to override the advanced search filters or if this is even possible.

How to apply-templates to all templates except a spefic one

In the code bellow I'm applying all the templates to the element chapter (in the end of the code) but I would like to know how it is possible to apply all the templates to this element except a spefic one. In this case it's the element title because I'm already selecting it in the line before and it appears repeated in the html file. Someone?
<xsl:template match="chapter">
<h3>
<a name="{#id}"><xsl:value-of select="title"/></a>
</h3>
<xsl:apply-templates/>
</xsl:template>
Output:
<h3>Title</h3>
Title<br>
Text.
A plain <xsl:apply-templates/> is equivalent to <xsl:apply-templates select="node()" />, i.e. all child nodes. You can exclude certain nodes by using the XPath 2.0 except operator, e.g.
<xsl:apply-templates select="node() except title" />
This would select all child nodes except those that are elements with the name title. If you are only interested in child elements (not text nodes etc.) then you could use * except title instead.
The except operator essentially implements set difference - you're not limited to simple element names on the right, you can use any expression that returns a sequence of nodes, e.g.
node() except (title | div[#class = 'heading'])
X except Y selects all nodes that are in the sequence selected by X and not also in the sequence selected by Y

XSL unique value key

Goal
(XSLT 1.0). My goal is to take a set of elements, S, and produce another set, T, where T contains the unique elements in S. And to do so as efficiently as possible. (Note: I don't have to create a variable containing the set, or anything like that. I just need to loop over the elements that are unique).
Example Input and Key
<!-- My actual input consists of a bunch of <Result> elements -->
<AllMyResults>
<Result>
<someElement>value</state>
<otherElement>value 2</state>
<subject>Get unique subjects!</state>
</Result>
</AllMyResults>
<xsl:key name="SubjectKey" match="AllMyResults/Result" use="subject"/>
I think the above works, but when I go to use my key, it is incredibly slow. Below is the code for how I use my key.
<xsl:for-each select="Result[count(. | key('SubjectKey', subject)[1]) = 1]">
<xsl:sort select="subject" />
<!-- Do something with the unique subject value -->
<xsl:value-of select="subject" />
</xsl:for-each>
Additional Info
I believe I am doing this wrong because it slowed down my XSL considerably. As some additional info, the code shown above is in a separate XSL file from my main XSL file. From the main XSL, I am calling a template that contains the xsl:key and the for-each shown above. The input to this template is an xsl:param containing my node-set (similar to the example input shown above).
I can't see any reason from the information given why the code should be slow. It might be worth seeing if the slowness is something that happens on all XSLT processors, or if it's peculiar to one.
Try substituting
count(. | key('SubjectKey', subject)[1]) = 1
with:
generate-id() = generate-id(key('SubjectKey', subject)[1])
In some XSLT processors the latter is much faster.