XSLT from XHTML+MathML to LATEX

Eitan M. Gurari

July 19, 2000

Contents

1 Example
2 Root
3 xhtml
4 mathml
5 xhtml+mathml

1 Example

The script

 xt latexexa.html xhm2latex.xsl latexexa-1.notex
 utf2any latexexa-1.notex latexexa-1.tex
 pdflatex latexexa-1

The main files:

Auxiliary files:

utf2any assumes:

2 Root

<..xhm2latex..>

       <?xml version="1.0"?>
       <xsl:stylesheet
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         version="1.0">
             <xsl:output method="text"/>
             <xsl:import href="xh2latex.xsl"/>
             <xsl:import href="mml2latex.xsl"/>
             <xsl:import href="xhmml2latex.xsl"/>
       </xsl:stylesheet>
       -_-_-

3 xhtml

<..xh2latex..>

       <?xml version="1.0"?>

       <xsl:stylesheet
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         xmlns:xhtml="http://www.w3.org/TR/xhtml1/transitional"
         version="1.0">
         <.latex documentclass template.>
         <.latex maketitle template.>
         <.latex sectioning template.>
         <.latex par template.>
         <.latex comment template.>
         <.latex bibliography template.>
         <.latex tabular template.>
         <.latex lists template.>
         <.latex graphics template.>
         <.latex figures template.>
         <.latex url template.>
         <.latex ref template.>
         <.latex label template.>
         <.latex fonts template.>
       </xsl:stylesheet>
       -_-_-

<..latex documentclass template..>

       <xsl:template match="xhtml:html">
        <xsl:text>
        \documentclass{article}
        <.usepackage graphics.>
        \begin{document}
        </xsl:text>
             <xsl:apply-templates select="xhtml:body"/>
        <xsl:text>\end{document}
        </xsl:text>
       </xsl:template>
       -_-_-

<..latex maketitle template..>

       <xsl:template match="xhtml:div[@class='maketitle']">
        <xsl:apply-templates mode="maketitle"/>
        <xsl:text>\maketitle </xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:div[@class='date']" mode="maketitle">
       </xsl:template>
       
       <xsl:template match="xhtml:h2[@class='titleHead']" mode="maketitle">
        <xsl:text>\title{</xsl:text>
          <xsl:apply-templates/>
        <xsl:text>}</xsl:text>

       </xsl:template>
       
       <xsl:template match="xhtml:div[@class='author']" mode="maketitle">
        <xsl:text>\author{</xsl:text>
          <xsl:apply-templates/>
        <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:div[@class='thanks']" mode="maketitle">
        <xsl:text>\def\thanks{</xsl:text>
          <xsl:apply-templates/>
        <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

<..latex sectioning template..>

       <xsl:template match="xhtml:h3[@class='sectionHead']">
        <xsl:text>\section{</xsl:text>
          <xsl:apply-templates/>
        <xsl:text>}</xsl:text>
        <.section label.>
       </xsl:template>
       
       <xsl:template match="xhtml:h4[@class='subsectionHead']">
        <xsl:text>\subsection{</xsl:text>
          <xsl:apply-templates/>
        <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:span[@class='titlemark']">
       </xsl:template>
       
       <xsl:template match="xhtml:h3[@class='likesectionHead']">
       <xsl:choose>
       <xsl:when test="following-sibling::*[position()=1
                         and self::xhtml:div[@class='thebibliography']]">
       </xsl:when>
       <xsl:otherwise>
           <xsl:text>\section*{</xsl:text>
             <xsl:apply-templates/>
           <xsl:text>}</xsl:text>
       </xsl:otherwise>
       </xsl:choose>
       </xsl:template>
       -_-_-

<..latex par template..>

       <xsl:template match="xhtml:p">
         <xsl:text>\par
           </xsl:text>
          <xsl:apply-templates/>
        <.inline end par.>
       </xsl:template>
       
       <xsl:template match="xhtml:p[@class='nopar']">
        <xsl:text>\empty </xsl:text>
          <xsl:apply-templates/>
        <xsl:text>\empty </xsl:text>
       </xsl:template>
       
       <xsl:template match="text()">
        <xsl:choose>
        <xsl:when test="parent::*[self::xhtml:p]">
          <.normalize text.>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="." />
        </xsl:otherwise>
        </xsl:choose>
       </xsl:template>
       -_-_-

<..inline end par..>

        <xsl:if  test="child::*[position()=last() and
                         .!='tex4ht:inline']">
          <xsl:choose>
          <xsl:when test="descendant::comment()[ .='tex4ht:inline']">
             <xsl:text>\empty </xsl:text>
          </xsl:when>
          <xsl:otherwise>
             <xsl:text>\par
             </xsl:text>
          </xsl:otherwise>
          </xsl:choose>
        </xsl:if>
       -_-_-

Spaces in body can translate to empty lines on backward translation.

<..latex par template..>+

       <xsl:template match="xhtml:body">
         <xsl:apply-templates select="*|@*|processing-instruction()|comment()"/>
       </xsl:template>
       -_-_-

<..normalize text..>

       <xsl:if test="substring(.,1,1)=' '
                  or substring(.,1,1)='&#x9;'
                  or substring(.,1,1)='&#xA;'
                  or substring(.,1,1)='&#xD;'
                  ">
          <xsl:text> </xsl:text>
       </xsl:if>
       <xsl:value-of select="normalize-space(.)" />
       <xsl:if test="substring(.,string-length(.),1)=' '
                  or substring(.,string-length(.),1)='&#x9;'
                  or substring(.,string-length(.),1)='&#xA;'
                  or substring(.,string-length(.),1)='&#xD;'
                  ">
          <xsl:text> </xsl:text>
       </xsl:if>
       -_-_-

<..latex comment template..>

       <xsl:template match="comment()">
       <xsl:if test="starts-with(.,'l. ')">
        <xsl:text>% </xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>
        </xsl:text>
       </xsl:if>
       </xsl:template>
       -_-_-

<..latex bibliography template..>

       <xsl:template match="xhtml:span[@class='cite']">
            <xsl:apply-templates select="xhtml:a" mode="cite"/>
       </xsl:template>
       
       <xsl:template match="xhtml:a" mode="cite">
        <xsl:text>\cite{</xsl:text>
            <xsl:value-of select="substring-after(@href,'#X')"/>
        <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:div[@class='thebibliography']">
         <xsl:text>\begin{thebibliography}{</xsl:text>
           <xsl:value-of
              select="count(descendant::xhtml:p[@class='bibitem']) * 10"/>
         <xsl:text>}</xsl:text>
          <xsl:apply-templates/>
         <xsl:text>\end{thebibliography}</xsl:text>

       </xsl:template>
       
       <xsl:template match="xhtml:p[@class='bibitem']">
         <xsl:text>\bibitem{</xsl:text>
          <xsl:apply-templates select="xhtml:a[1]" mode="bibitem"/>
         <xsl:text>}</xsl:text>
         <xsl:apply-templates/>
       </xsl:template>
       
       <xsl:template match="xhtml:a" mode="bibitem">
            <xsl:value-of select="substring-after(@name,'X')"/>
       </xsl:template>
       
       <xsl:template
          match="xhtml:span[@class='biblabel']|xhtml:p[@class='bibitem']/a[1]">
       </xsl:template>
       -_-_-

<..latex tabular template..>

       <xsl:template match="xhtml:table[@class='tabular'
                                     or @class='inline-tabular']">
          <xsl:if test="@class='tabular'">
             <xsl:text>\par </xsl:text>
          </xsl:if>
          <xsl:text>\begin{tabular}{</xsl:text>
            <xsl:value-of select="comment()[last()]"/>
          <xsl:text>}</xsl:text>
            <xsl:apply-templates select="xhtml:tr" mode="tabular"/>
          <xsl:text>\end{tabular}</xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:tr" mode="tabular">
          <xsl:apply-templates select="xhtml:td" mode="tabular"/>
          <xsl:if test="position()!=last()">
            <xsl:text>\\
            </xsl:text>
          </xsl:if>
       </xsl:template>
       
       <xsl:template match="xhtml:td" mode="tabular">
          <xsl:if test="position()!=1">
            <xsl:text>&%
            </xsl:text>
          </xsl:if>
          <xsl:apply-templates/>
       </xsl:template>

       
       <xsl:template match="xhtml:tr[@class='hline']" mode="tabular">
          <xsl:text>\hline </xsl:text>
       </xsl:template>
       <xsl:template match="xhtml:tr[@class='hline']">
          <xsl:text>\hline </xsl:text>
       </xsl:template>
       -_-_-

<..latex lists template..>

       <xsl:template match="xhtml:ol[@class='enumerate1']">
          <xsl:text>\begin{enumerate}</xsl:text>
          <xsl:for-each select="xhtml:li">
             <xsl:text>\item </xsl:text>
             <xsl:apply-templates />
          </xsl:for-each>
          <xsl:text>\end{enumerate}</xsl:text>
       </xsl:template>
       -_-_-

<..usepackage graphics..>

       \usepackage{graphicx}
       \DeclareGraphicsRule{.gif}{eps}{}{}
       -_-_-

<..latex graphics template..>

       <xsl:template match="xhtml:img[@class='graphics'
                                   or @class='includegraphics']">
         <xsl:text>\includegraphics[width=</xsl:text>
           <xsl:value-of select="@width"/>
         <xsl:text>,height=</xsl:text>
           <xsl:value-of select="@height"/>
         <xsl:text>]{</xsl:text>
           <xsl:value-of select="@src"/>
         <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

<..latex figures template..>

       <xsl:template match="xhtml:td[@class='figure']">
          <xsl:text>\begin{figure}</xsl:text>
          <xsl:apply-templates/>
          <xsl:text>\end{figure}</xsl:text>
       </xsl:template>
       
       <xsl:template match="xhtml:div[@class='caption']">
         <xsl:apply-templates select="xhtml:table" mode="caption" />
       </xsl:template>

       
       <xsl:template match="xhtml:table" mode="caption">
         <xsl:apply-templates select="xhtml:tr" mode="caption" />
       </xsl:template>
       
       <xsl:template match="xhtml:tr" mode="caption">
         <xsl:apply-templates select="xhtml:td[2]" mode="caption" />
       </xsl:template>
       
       <xsl:template match="xhtml:td" mode="caption">
         <xsl:text>\caption{</xsl:text>
         <xsl:apply-templates />
         <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

<..latex ref template..>

       <xsl:template match="xhtml:a" >
         <xsl:choose>
         <xsl:when test="descendant::comment()[starts-with(.,'tex4ht:ref:')]">
            <xsl:text>\ref{</xsl:text>
            <xsl:value-of select="substring-after(@href,'#')"/>
            <xsl:text>}% </xsl:text>
            <xsl:apply-templates mode="ref"
               select="descendant::comment()[starts-with(.,'tex4ht:ref:')]" />
            <xsl:text>
            </xsl:text>
         </xsl:when>
         <xsl:otherwise>
            <xsl:text>\empty </xsl:text>
            <xsl:apply-templates />
         </xsl:otherwise>
         </xsl:choose>
       </xsl:template>
       <xsl:template  mode="ref"
          match="comment()[starts-with(.,'tex4ht:ref:')]">
            <xsl:value-of select="substring-after(.,'tex4ht:ref:')"/>
       </xsl:template>
       -_-_-

<..latex label template..>

       <xsl:template match="comment()[starts-with(.,'tex4ht:label?:')]">
          <xsl:text>\label{\empty </xsl:text>
            <xsl:value-of select="substring-after(.,'tex4ht:label?:')"/>
          <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

The \empty is to avoid unwanted empty lines.

<..latex fonts template..>

       <xsl:template match="xhtml:span[@class='cmtt-10']">
         <xsl:text>{\tt </xsl:text>
         <xsl:apply-templates />
         <xsl:text>}</xsl:text>
       </xsl:template>
       <xsl:template match="xhtml:span[@class='cmti-10']">
         <xsl:text>{\it </xsl:text>
         <xsl:apply-templates />
         <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

4 mathml

<..mml2latex..>

       <?xml version="1.0"?>
       <xsl:stylesheet
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         xmlns:mml="http://www.w3.org/1998/Math/MathML"
         exclude-result-prefixes="mml"
         version="1.0">
         <.latex math delimiters template.>
         <.latex sub/sup template.>
         <.latex mfrac template.>
         <.latex mi template.>
         <.latex eqnarray template.>
         <.latex parentheses template.>
         <.latex array template.>
         <.mml latex accents.>
         <.mml latex templates.>
         <.mml label templates.>
       </xsl:stylesheet>
       -_-_-

<..latex math delimiters template..>

       <xsl:template match="mml:math" >
       <xsl:choose>
       <xsl:when test="mml:mtable[@class='eqnarray']
                   or  mml:mtable[@class='eqnarray-star']   ">
          <xsl:text>\empty</xsl:text>
          <xsl:apply-templates />
          <xsl:text>\empty</xsl:text>

       </xsl:when>
       <xsl:otherwise>
          <xsl:if test="@display='block'"><xsl:text>$</xsl:text></xsl:if>
          <xsl:text>$</xsl:text>
          <xsl:apply-templates/>
          <xsl:if test="@display='block'"><xsl:text>$</xsl:text></xsl:if>
          <xsl:text>$</xsl:text>
       </xsl:otherwise>
       </xsl:choose>
       </xsl:template>
       
       <xsl:template match="mml:mrow" >
       <xsl:text>{</xsl:text>
       <xsl:apply-templates/>
       <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:mrow[@class='mbox']" >
       <xsl:text>\mbox{</xsl:text>
       <xsl:apply-templates select="mml:mtext"/>
       <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

<..latex sub/sup template..>

       <xsl:template match="mml:msup" >
       <xsl:call-template name="base-scription" />
       <xsl:text>^
         {</xsl:text>
       <xsl:apply-templates select="*[2]"/>
       <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:msub" >
       <xsl:call-template name="base-scription" />
       <xsl:text>_
         {</xsl:text>
       <xsl:apply-templates select="*[2]"/>
       <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:msubsup" >
       <xsl:call-template name="base-scription" />
       <xsl:text>_
         {</xsl:text>
       <xsl:apply-templates select="*[2]"/>

       <xsl:text>}^
         {</xsl:text>
       <xsl:apply-templates select="*[3]"/>
       <xsl:text>}</xsl:text>
       </xsl:template>
       
       <xsl:template name="base-scription">
       <xsl:choose>
       <xsl:when test="child::*[position()=1 and
          self::*[string-length(.)=1 and position()=1 and position()=last()]]">
          <xsl:apply-templates select="*[1]"/>
       </xsl:when>
       <xsl:otherwise>
          <xsl:text>{</xsl:text>
          <xsl:apply-templates select="*[1]"/>
          <xsl:text>}</xsl:text>
       </xsl:otherwise>
       </xsl:choose>
       </xsl:template>
       -_-_-

In some cases, such as \sum, we don’t want braces around the base so that the subscripts and superscripts will be, respectively, placed under and over the operator.

<..latex mfrac template..>

       <xsl:template match="mml:mfrac" >
        <xsl:text>\frac
        </xsl:text>
        <xsl:for-each select="*">
          <xsl:text>{</xsl:text>
          <xsl:apply-templates/>
          <xsl:text>}</xsl:text>
        </xsl:for-each>
       </xsl:template>
       -_-_-

<..latex mi template..>

       <xsl:template match="mml:mi[@class='mathrm']">
          <xsl:text>{\mathrm{</xsl:text>
          <xsl:apply-templates/>
          <xsl:text>}}</xsl:text>
       </xsl:template>
       <xsl:template match="mml:mo[@class='csname']">
          <xsl:text>\</xsl:text>
          <xsl:value-of select="."/>
          <xsl:text> </xsl:text>

       </xsl:template>
       -_-_-

<..latex eqnarray template..>

       <xsl:template match="mml:mtable[@class='eqnarray']">
          <xsl:text>\begin{eqnarray}</xsl:text>
            <xsl:apply-templates select="mml:mtr" mode="eqnarray"/>
          <xsl:text>\end{eqnarray}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:mtable[@class='eqnarray-star']">
          <xsl:text>\begin{eqnarray*}</xsl:text>
            <xsl:apply-templates select="mml:mtr" mode="eqnarray"/>
          <xsl:text>\end{eqnarray*}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:mtr" mode="eqnarray">
          <xsl:apply-templates select="mml:mtd" mode="eqnarray"/>
          <xsl:if test="position()!=last()">
            <xsl:text>\\
            </xsl:text>
          </xsl:if>
       </xsl:template>
       
       <xsl:template match="mml:mtd" mode="eqnarray">
          <xsl:if test="position()!=1">
            <xsl:text>&%
            </xsl:text>
          </xsl:if>
          <xsl:apply-templates/>
       </xsl:template>
       
       <xsl:template match="mml:mtd[@class='eqnarray-4']" mode="eqnarray">
          <xsl:if test="child::mml:mtext[.='' and @class='eqnarray']">
              <xsl:text>\nonumber </xsl:text>
          </xsl:if>
          <xsl:apply-templates select="mml:mtext[@class='label']" />
       </xsl:template>
       -_-_-

<..latex parentheses template..>

       <xsl:template match="mml:mfenced">
          <xsl:choose>
            <xsl:when test="@open">
               <xsl:value-of select="concat('\left',@open)"/>
            </xsl:when>
            <xsl:otherwise> <xsl:text>\left.</xsl:text>

            </xsl:otherwise>
          </xsl:choose>
          <xsl:apply-templates/>
          <xsl:choose>
            <xsl:when test="@close">
               <xsl:value-of select="concat('\right',@close)"/>
            </xsl:when>
            <xsl:otherwise> <xsl:text>\right.</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
       </xsl:template>
       -_-_-

<..latex array template..>

       <xsl:template match="mml:mtable[@class='array']">
          <xsl:text>\begin{array}{</xsl:text>
            <xsl:value-of select="comment()[last()]"/>
          <xsl:text>}</xsl:text>
            <xsl:apply-templates select="mml:mtr" mode="array"/>
          <xsl:text>\end{array}</xsl:text>
       </xsl:template>
       
       <xsl:template match="mml:mtr" mode="array">
          <xsl:apply-templates select="mml:mtd" mode="array"/>
          <xsl:if test="position()!=last()">
            <xsl:text>\\
            </xsl:text>
          </xsl:if>
       </xsl:template>
       
       <xsl:template match="mml:mtd" mode="array">
          <xsl:if test="position()!=1">
            <xsl:text>&%
            </xsl:text>
          </xsl:if>
          <xsl:apply-templates/>
       </xsl:template>
       -_-_-

<..mml latex accents..>

       <xsl:template match="mml:munderover[@accent='true']">
       <xsl:choose>
       <xsl:when test="
          child::mml:mrow[2]= ''
        and
          child::mml:mrow[position()=3 and child::mml:mo='&#x0304;']
        ">

          <xsl:text>\bar{</xsl:text>
             <xsl:apply-templates select="*[1]" />
          <xsl:text>}</xsl:text>
       </xsl:when>
       <xsl:otherwise>
          <xsl:apply-templates />
       </xsl:otherwise>
       </xsl:choose>
       </xsl:template>
       -_-_-

<..mml latex templates..>

       <xsl:template match="mml:mspace[@width]">
         <xsl:choose>
         <xsl:when  test="@width!='0pt' and @width!='0.0pt'">
            <xsl:text>\hspace{</xsl:text>
               <xsl:value-of select="@width"/>
            <xsl:text>}</xsl:text>
         </xsl:when>
         <.when mspace labels.>
         <xsl:otherwise>
            <xsl:text>\empty </xsl:text>
         </xsl:otherwise>
         </xsl:choose>
       </xsl:template>
       <xsl:template match="mml:mspace[@class='nbsp']">
         <xsl:text>\  </xsl:text>
       </xsl:template>
       <.mml mspace labels.>
       -_-_-

<..mml latex templates..>+

       <xsl:template match="mml:msqrt">
         <xsl:text>\sqrt{</xsl:text>
            <xsl:apply-templates />
         <xsl:text>}</xsl:text>
       </xsl:template>
       -_-_-

<..when mspace labels..>

       <xsl:when test="@class='label'">
         <xsl:apply-templates select="." mode="mspace-label" />
       </xsl:when>
       -_-_-

<..mml mspace labels..>

       <xsl:template match="mml:mspace" mode="mspace-label">
         <xsl:text>\empty </xsl:text>

       </xsl:template>
       -_-_-

<..xhtml-mml mspace labels..>

       <xsl:template match="mml:mspace[@class='label']" mode="mspace-label">
         <xsl:variable  name="var" select="concat('#',@id)" />
         <xsl:if test="<.href?.>">
            <xsl:text>\label{</xsl:text>
            <xsl:value-of select="@id" />
            <xsl:text>}</xsl:text>
          </xsl:if>
       </xsl:template>
       -_-_-

<..href?..>

       //xhtml:a[@href=$var]
       -_-_-

xt seems to not recognize the key function

<..href?-NO..>

       key('refs',$var)
       -_-_-

<..keys..>

       <xsl:key name="refs"  match="xhtml:a" use="@href" />
       -_-_-

<..section label..>

       <xsl:apply-templates select="xhtml:a[@name]" mode="section-label"/>
       -_-_-

<..latex label template..>+

       <xsl:template match="xhtml:a[@name]" mode="section-label">
         <xsl:variable  name="var" select="concat('#',@name)" />
         <xsl:if test="<.href?.>">
            <xsl:text>\label{</xsl:text>
            <xsl:value-of select="@name" />
            <xsl:text>}</xsl:text>
          </xsl:if>
       </xsl:template>
       -_-_-

<..mml label templates..>

       <xsl:template match="mml:mtext[@class='label']">
          <xsl:text>\label{</xsl:text>
            <xsl:value-of select="@id" />

          <xsl:text>}</xsl:text>
       </xsl:template>
       <xsl:template match="mml:mtext[@class='endlabal']
                         | mml:mspace[@class='endlabal']">
       </xsl:template>
       -_-_-

5 xhtml+mathml

<..xhmml2latex..>

       <?xml version="1.0"?>
       <xsl:stylesheet
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         xmlns:mml="http://www.w3.org/1998/Math/MathML"
         xmlns:xhtml="http://www.w3.org/TR/xhtml1/transitional"
         exclude-result-prefixes="mml"
         version="1.0">
         <.xhmml latex equation template.>
         <.xhtml-mml mspace labels.>
       </xsl:stylesheet>
       -_-_-

<..xhmml latex equation template..>

       <xsl:template match="xhtml:table[@class='equation']">
          <xsl:text>\begin{equation}</xsl:text>
          <xsl:apply-templates select="descendant::*[parent::mml:math]" />
          <xsl:text>\end{equation}</xsl:text>
       </xsl:template>
       -_-_-