Feargal Hogan
Feargal Hogan

Reputation: 301

Converting 'typeset' footnotes to docbook style

I have a bunch of content containing calstables where the footnoting has been done using typesetting techniques, with each of the indices set using <sup/> elements and each of the footnotes included in its own straddle row at the end of the table.

I want to convert these to use Docbook footnote markup like this and this

The sample data is as follows (I have added <from/> and <to/> elements to show where it is coming from and where it needs to go to)

<testdata>
   <from>
      <table>
         <tgroup cols="3">
            <colspec colname="1" colnum="1" colwidth="39pt" align="left"/>
            <colspec colname="2" colnum="2" colwidth="39pt" align="center"/>
            <colspec colname="3" colnum="3" colwidth="39pt" align="center"/>
            <thead>
               <row valign="bottom">
                  <entry>Item</entry>
                  <entry>ItemA</entry>
                  <entry>ItemB<sup>1</sup></entry>
               </row>
            </thead>
            <tbody>
               <row valign="top">
                  <entry>Entry 1</entry>
                  <entry>60</entry>
                  <entry>3.2</entry>
               </row>
               <row>
                  <entry>Entry A</entry>
                  <entry>150</entry>
                  <entry>3.55<sup>2</sup></entry>
               </row>
               <row>
                  <entry>This entry</entry>
                  <entry>260<sup>3</sup></entry>
                  <entry>3.55<sup>2</sup></entry>
               </row>
               <row>
                  <entry align="left" namest="1" nameend="3"><sup>1</sup> LAT</entry>
               </row>
               <row>
                  <entry align="left" namest="1" nameend="3"><sup>2</sup> Itemvalue &lt;24.5 m. Also see note below.</entry>
               </row>
               <row>
                  <entry align="left" namest="1" nameend="3"><sup>3</sup> Ramp up 19.8 m.</entry>
               </row>
            </tbody>
         </tgroup>
      </table>   
   </from>
   <to>
      <table>
         <tgroup cols="3">
            <colspec colname="1" colnum="1" colwidth="39pt" align="left"/>
            <colspec colname="2" colnum="2" colwidth="39pt" align="center"/>
            <colspec colname="3" colnum="3" colwidth="39pt" align="center"/>
            <thead>
               <row valign="bottom">
                  <entry>Item</entry>
                  <entry>ItemA</entry>
                  <entry>ItemB<footnote id="1">LAT</footnote></entry>
               </row>
            </thead>
            <tbody>
               <row valign="top">
                  <entry>Entry 1</entry>
                  <entry>60</entry>
                  <entry>3.2</entry>
               </row>
               <row>
                  <entry>Entry A</entry>
                  <entry>150</entry>
                  <entry>3.55<footnote id="2">Itemvalue &lt;24.5 m. Also see note below.</footnote></entry>
               </row>
               <row>
                  <entry>This entry</entry>
                  <entry>260<footnote id="3">Ramp up 19.8 m.</footnote></entry>
                  <entry>3.55<footnoteref linkend="2"/></entry>
               </row>
            </tbody>
         </tgroup>
      </table>   
   </to>
</testdata>

The logic is reasonably straightforward to articulate:

1. Copy everthing to output, unless ...
2. it contains a `<sup/>` element, in which case either
    1. if it has a `@nameend` attribute, do nothing, or 
    2. if it is the first instance of this index, create a footnote element with an `@id` attribute, grabbing the content from the matching straddle row, or
    3. if it's not the first instance, create a footnoteref element, with a matching `@linkend` attribute

Of course there is a bunch of error checking too, but I'm not too worried about that now.

I can address each of #2 above, using a bunch of individual match patterns like

<xsl:template match="sup[text() = '1'][1]">
<xsl:template match="sup[text() = '2'][1]">
<xsl:template match="sup[text() = '3'][1]">

but I figure there must be a more elegant match pattern (perhaps using keys?) to match the first instance of each index used, but for the life of me I can't think what it might be.

I have defined 2 keys so far

<xsl:key name="fn-indices" match="sup" use="number(.)"/>
<xsl:key name="fn-text" match="entry[sup][@nameend &gt; @namest]" use="number(sup)"/>

But I'm not sure how best to use them?

Any suggestions for an elegant match pattern for the 1st instance of each index?

Upvotes: 0

Views: 44

Answers (2)

Feargal Hogan
Feargal Hogan

Reputation: 301

So I've come up with this template which seems to do what I want

Note: I have shortened footnote to fn and footnoteref to fnref to avoid conflicts with other processes for the moment.

Also instead of using is I have used generate-id() which is 1.0 compliant but I will probably change this in due course

<xsl:template match="sup" mode="sup2fn" priority="10">
    <xsl:variable name="index" select="number(normalize-space(.))"/>
    <xsl:variable name="this-table" select="ancestor::*[local-name() = 'table']"/>
    <xsl:variable name="fn-text-nodes" select="key('fn-text',$index)[generate-id(ancestor::*[local-name() = 'table']) = generate-id($this-table)][1]"/>
    <xsl:variable name="idref-here" select="generate-id(.)"/>
    <xsl:variable name="instance-1" select="key('fn-indices',$index)[generate-id(ancestor::*[local-name() = 'table']) = generate-id($this-table)][1]"/>
    <xsl:variable name="idref-target" select="generate-id($instance-1)"/>
    <xsl:variable name="fn-text">
        <xsl:apply-templates select="$fn-text-nodes/node()" mode="make-footnote-text"/>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="generate-id(.) = generate-id($instance-1) ">
            <fn id="{$idref-here}">
                <xsl:if test="$debug = 'yes'">
                    <xsl:attribute name="index"><xsl:value-of select="$index"/></xsl:attribute>
                </xsl:if><xsl:value-of select="normalize-space($fn-text)"/></fn>
        </xsl:when>
        <xsl:otherwise>
            <fnref linkend="{$idref-target}">
                <xsl:if test="$debug = 'yes'">
                    <xsl:attribute name="index"><xsl:value-of select="$index"/></xsl:attribute>
                </xsl:if>
            </fnref>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

I have had to account for the possibility of multiple tables too, so I am testing that that the sup nodes have the same parent. I wonder if that might be more efficient in the key definition?

Also I have maintatined the original index for debug purposes, as well as creating new id attributes Lastly, instead of using seperate match patterns, I have a single sup template and have used an <xsl:choose> branch in there to test if this is the first or a subsequent instance of <sup>

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167781

sup[. is key('fn-indices', number())[1]] should match the first item in each "group" so you should be able to replace

<xsl:template match="sup[text() = '1'][1]">
<xsl:template match="sup[text() = '2'][1]">
<xsl:template match="sup[text() = '3'][1]">

with

<xsl:template match="sup[. is key('fn-indices', number())[1]]">

The is operator checks node identity.

Upvotes: 1

Related Questions