Search code examples
xslttei

XSLT output to HTML: add an HTML element with incremented number, based on another element


I have tei:xml documents which I am transforming into HTML with XSLT 2.0. The effective structure of the tei documents look like this:

...
<p xml:lang="LA">
  <seg type="a" corresp="#fooid"><date type="doc_date" when="1245"/>In non hendrerit metus. Sed in 
       posuere eros, sit amet pharetra lacus.</seg>
  <seg type="a">Nullam semper varius justo, vitae mollis turpis 
       dapibus sit amet. Donec<note type="public_note"></note> 
       rhoncus tempor urna sit amet 
       imperdiet.</seg>
  <seg type="a">Integer id ante nunc. Curabitur at ligula sed 
       arcu consequat gravida et id orci. Morbi quis porta 
       dolor.</seg>
  <seg type="a" corresp="#fooid2">Sed dictum<note type="public_note"> 
       sem nec urna sodales cursus. Donec sit amet nibh tempor, 
       congue ligula semper, rhoncus odio.</seg>
<p>
...

In several <xsl:template>s I transform the xml to HTML and thencycle through the tei document to identify elements that need to be transformed into superscript footnote numbers. I use the <xsl:number function> to increment the number:

 <xsl:template match="p">
   <div><xsl:apply-templates></div>
 </xsl:template>

 <xsl:template match="seg[@type='a']">
   <p><xsl:apply-templates></p>
 </xsl:template>

 <xsl:template match="seg//date[@type='doc_date'] | 
   seg//note[@type='public_note']">
     <sup>
       <xsl:number count="seg//date[@type='doc_date'] | 
          seg//note[@type='public_note']" format="1" level="any"/>
     </sup>
 </xsl:template>

Producing three <sup/> with incremental values 1, 2, 3:

<div>
   <p><sup>1</sup>In non hendrerit metus. Sed in 
       posuere eros, sit amet pharetra lacus.</p>
   <p>Nullam semper varius justo, vitae mollis turpis 
       dapibus sit amet. Donec<sup>2</sup> rhoncus tempor 
       urna sit amet imperdiet.</p>
   <p>Integer id ante nunc. Curabitur at ligula sed 
       arcu consequat gravida et id orci. Morbi quis porta 
       dolor.</p>
   <p>Sed dictum sem<sup>3</sup> nec urna sodales cursus. 
      Donec sit amet nibh tempor, congue ligula semper, 
      rhoncus odio.</p>
<div>

The problem that I can't seem to solve is how to output the following, where <sup> is added AFTER <p> (based on <tei:seg>) when the condition seg[@corresp] is met:

<div>
   <p><sup>1</sup>In non hendrerit metus. Sed in 
       posuere eros, sit amet pharetra lacus.</p><sup>2</sup>
   <p>Nullam semper varius justo, vitae mollis turpis 
       dapibus sit amet. Donec<sup>3</sup> rhoncus tempor 
       urna sit amet imperdiet.</p>
   <p>Integer id ante nunc. Curabitur at ligula sed 
       arcu consequat gravida et id orci. Morbi quis porta 
       dolor.</p>
   <p>Sed dictum sem<sup>4</sup> nec urna sodales cursus. 
      Donec sit amet nibh tempor, congue ligula semper, 
      rhoncus odio.</p><sup>5</sup>
<div>

I can get them to work in separate templates (while creating the html <p/>), but not in one template. However, being in separate templates restarts the numbering.

Many thanks in advance.


Solution

  • You could use a template in a different mode to create number and include the seg[@corresp] elements in the pattern (as I have done in https://xsltfiddle.liberty-development.net/pPqsHUb) but as xsl:number works based on the position of nodes in the source document you don't get the order you have indicated as basically the seg[@corresp] elements that way are numbered with lower numbers as their child or descendant date or note elements.

    So basically I think you need to run a two step transformation, add a note or date or other marker element at the end of the seg[@corresp], then number them and the note/date in a second step:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">
    
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:output method="html" indent="yes" html-version="5"/>
    
      <xsl:mode name="add-marker" on-no-match="shallow-copy"/>
    
      <xsl:template match="seg[@corresp]" mode="add-marker">
          <xsl:next-match/>
          <marker/>
      </xsl:template>
    
      <xsl:variable name="markers-added">
          <xsl:apply-templates mode="add-marker"/>
      </xsl:variable>
    
     <xsl:template match="/">
         <xsl:apply-templates select="$markers-added/node()"/>
     </xsl:template>
    
     <xsl:template match="p">
       <div><xsl:apply-templates/></div>
     </xsl:template>
    
     <xsl:template match="seg[@type='a']">
       <p><xsl:apply-templates/></p>
     </xsl:template>
    
     <xsl:template match="seg//date[@type='doc_date'] | 
       seg//note[@type='public_note'] | marker">
         <xsl:apply-templates select="." mode="number"/>
     </xsl:template>
    
     <xsl:template match="*" mode="number">
         <sup>
            <xsl:number count="marker | seg//date[@type='doc_date'] | 
              seg//note[@type='public_note']" format="1" level="any"/>         
         </sup>
     </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/pPqsHUb/1

    I have used the XSLT 3 xsl:mode declaration in there but it could be replaced by the identity transformation template e.g.

    <xsl:template match="@* | node()" mode="add-marker">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()" mode="#current"/>
      </xsl:copy>
    </xsl:template>
    

    for an XSLT 2 processor.