Newer
Older
XML / modules / hyperlinks.xml
<?xml version="1.0" encoding="utf-8"?>

<!--
    Hyperlinks and URLs. The content of the hyperlink element becomes the link text. All hyperlink elements have these attributes:
    
    @url: A URL that the hyperlink links to. If no link text is provided, the URL is used as the link text.
    
    @label: A label that the hyperlink links to. The value should be an acceptable label in both LaTeX and HTML.
    
    @target: The target tab/window to open the hyperlink in. Only applies to (X)HTML.
-->

<stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">


    <!--
        A hyperlink with an HTTP URL and some combination of label (page anchor) and content (link text).
        
        @url:       The URL to link to. [required]
        @label:     The name of the anchor to link to in the URL (i.e., url#label). [optional]
        @target:    The target of the URL (e.g., _blank). Only relevant to (X)HTML. [optional]
    -->
    <template name="hyperlink" match="hyperlink[@url]">
        <!--
            In very rare circumstances, hyperref can get munged in such a way that it doesn't correctly escape the # character for the anchor (e.g., an \href inside a \resizebox; why? I don't know). We therefore need to escape it manually, which produces the correct output regardless. The best way to do this is construct the full URL first, then replace "#" with "\#" in (Xe)LaTeX; this also catches hyperlinks where the author has embedded the anchor in the @url attribute.
        -->
        <common>
            <xsl:variable name="fullURL">
                <xsl:value-of select="@url" />
                <xsl:if test="@label">
                    <xsl:text>#</xsl:text>
                    <xsl:value-of select="@label" />
                </xsl:if>
            </xsl:variable>
        </common>
        <common formats="/latex/xelatex/">
            <xsl:choose>
                <!--
                    Just a bare URL with no link text.
                -->
                <xsl:when test="not( node() )">
                    <xsl:text>\url{</xsl:text>
                    <xsl:value-of select="replace( $fullURL, '#', '\\#' )" />
                    <xsl:text>}</xsl:text>
                </xsl:when>
                <xsl:otherwise>
                    <!--
                        Sometimes the hyperlink content may be explicitly split across multiple lines, separated by <br> elements. This isn't a problem in (X)HTML, but it breaks (Xe)LaTeX, because you can't include a \\ inside most macros. The solution is to explicitly process the sub-elements, and wrap appropriate hyperref macros around them, /except/ for any embedded <br> elements.
                        
                        We can't just call hyperlink-internal, because we still want to process the <br> elements.
                    -->
                    <xsl:for-each select="node()">
                        <xsl:if test="not( self::br )">
                            <xsl:text>\href{</xsl:text>
                            <xsl:value-of select="replace( $fullURL, '#', '\\#' )" />
                            <xsl:text>}{</xsl:text>
                        </xsl:if>
                        <xsl:apply-templates select="." />
                        <xsl:if test="not( self::br )">
                            <xsl:text>}</xsl:text>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:otherwise>
            </xsl:choose>
        </common>
        <common formats="/html/xhtml/">
            <!-- We can't just call hyperlink-internal, because there may be an @target attribute. -->
            <a href="{$fullURL}">
                <xsl:if test="@target">
                    <xsl:attribute name="target">
                        <xsl:value-of select="@target" />
                    </xsl:attribute>
                </xsl:if>
                <xsl:choose>
                    <xsl:when test="not( node() )">
        				<code><xsl:value-of select="$fullURL" /></code>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates />
                    </xsl:otherwise>
                </xsl:choose>
            </a>
        </common>
    </template>
    
    
    <!--
        Special case: hyperlink with a label and content but no URL. These represent internal links within the document, and require different code in (Xe)LaTeX (need to use the full \hyperref macro instead of \href).
        
        @label:     The name of the label to link to (will become #label in (X)HTML). [required]
        @target:    The target of the URL (e.g., _blank). Only relevant to (X)HTML. [optional]
    -->
    <template name="hyperlink-label" match="hyperlink[@label and node() and not( @url )]">
        <!-- Note: not safe to use the url package here because \url{...} is fragile. -->
        <common formats="/latex/xelatex/">
            <xsl:for-each select="node()">
                <xsl:if test="not( self::br )">
                    <xsl:text>\hyperref[</xsl:text>
                    <xsl:value-of select="../@label" />
                    <xsl:text>]{</xsl:text>
                </xsl:if>
                <xsl:apply-templates select="." />
                <xsl:if test="not( self::br )">
                    <xsl:text>}</xsl:text>
                </xsl:if>
            </xsl:for-each>
        </common>
        <common formats="/html/xhtml/">
            <a href="#{@label}">
                <xsl:if test="@target">
                    <xsl:attribute name="target">
                        <xsl:value-of select="@target" />
                    </xsl:attribute>
                </xsl:if>
                <xsl:apply-templates />
            </a>
        </common>
    </template>
    
    
    <!-- ERROR: hyperlink has a label, but no URL and no content. -->
    <template name="empty-hyperlink-label" match="hyperlink[@label and not( node() ) and not( @url )]">
        <common>
            <xsl:message terminate="yes">&lt;hyperlink&gt; with @label and no @url must include link text.</xsl:message>
        </common>
    </template>
    
    
    <!-- Internal parameterised hyperlink templates to be called by the likes of the empty Oracle documentation elements. -->
    <template name="hyperlink-internal" match="hyperlink" mode="hyperlink-internal">
        <common>
            <xsl:param name="url" />
            <xsl:param name="label" />
        </common>
        <common formats="/latex/xelatex/">
            <xsl:text>\href{</xsl:text>
            <xsl:value-of select="$url" />
            <xsl:text>}{</xsl:text>
            <xsl:apply-templates select="exsl:node-set($label)" />
            <xsl:text>}</xsl:text>
        </common>
        <common formats="/html/xhtml/">
            <a href="{$url}"><xsl:apply-templates select="exsl:node-set($label)" /></a>
        </common>
    </template>

    <template name="empty-hyperlink-url-internal" match="hyperlink" mode="empty-hyperlink-internal">
        <common>
            <xsl:param name="url" />
        </common>
        <common formats="/latex/xelatex/">
            <xsl:text>\url{</xsl:text>
            <xsl:value-of select="$url" />
            <xsl:text>}</xsl:text>
        </common>
        <common formats="/html/xhtml/">
            <a href="{$url}"><code><xsl:value-of select="$url" /></code></a>
        </common>
    </template>
    
    
    <!-- A plain URL (i.e., not necessarily a hyperlink). -->
    <template name="url" match="url|uri|email|e-mail|email-address|e-mail-address">
        <!--
            \url in Hyperref will give us an active linke, so use \nolinkurl instead.
        -->
        <common formats="/latex/xelatex/">
            <xsl:text>\nolinkurl{</xsl:text>
            <xsl:apply-templates />
            <xsl:text>}</xsl:text>
        </common>
        <common formats="/html/xhtml/">
            <code><xsl:apply-templates /></code>
        </common>
    </template>


</stylesheet>