SE Does Not Like Dissent
SE Does Not Like Dissent

Reputation: 1835

XSLT 1.0 replacing tab character with a HTML element containing a tab

I've been bashing my head against XSLT character replacement with HTML elements to no avail and can't for the life of me figure out how to get XSLT to replace a tab with a span element containing a tab.

What I have within an XHTML document is a pre element that contains tab characters, and I found an XSLT string replace template function which I'm using to try to replace the tabs with tabs surrounded by spans:

    <xsl:template match="xhtml:pre">
        <xsl:element name="{local-name()}">

            <xsl:variable name="BodyText"><span class="tabspan">&#x9;</span></xsl:variable>

            <xsl:call-template name="replace">
                <xsl:with-param name="text" select="@*|node()"/>
                <xsl:with-param name="search" select="'&#x9;'"/>
                <xsl:with-param name="replace" select="$BodyText"/>
            </xsl:call-template>

        </xsl:element>
    </xsl:template>

    <!-- Replacement template function for XSLT 1.0 -->
    <xsl:template name="replace">
        <xsl:param name="text"/>
        <xsl:param name="search"/>
        <xsl:param name="replace"/>
        <xsl:choose>
            <xsl:when test="contains($text, $search)">
                <xsl:variable name="replace-next">
                    <xsl:call-template name="replace">
                        <xsl:with-param name="text" select="substring-after($text, $search)"/>
                        <xsl:with-param name="search" select="$search"/>
                        <xsl:with-param name="replace" select="$replace"/>
                    </xsl:call-template>
                </xsl:variable>
                <xsl:value-of select="concat(substring-before($text, $search),$replace, $replace-next)"/>
            </xsl:when>
            <xsl:otherwise>
                <span class="tabspan">
                    <xsl:value-of select="$text"/>
                </span>
            </xsl:otherwise>
        </xsl:choose>

However, no matter how I change the code, the span elements are either missing in the output (if I try to assign it via an xsl:variable like above) or it doesn't properly convert the XML escaped characters representing the angular brackets

&gt;
&lt;

Despite bizarrely converting

&quot;

To a quotation mark. I'm admittedly confused and don't know enough about XSLT 1.0. XSLT 2.0, 3.0 and EXSLT are not an option due to the limitations of the converting system.

Upvotes: 0

Views: 1890

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117100

Consider the following minimal example:

Input

<html xmlns="http://www.w3.org/1999/xhtml">
    <head/>
    <body>
    <p>This is an example.</p>
    <pre>Text before    text in the middle  text after</pre>
    </body>
</html>

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="xhtml:pre">
    <xsl:copy>
        <xsl:call-template name="replace-tabs">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
    </xsl:copy>
</xsl:template> 

<xsl:template name="replace-tabs">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '&#9;')">
            <xsl:value-of select="substring-before($text, '&#9;')"/>
            <span class="tabspan">
                <xsl:text>&#9;</xsl:text>
            </span>
            <xsl:call-template name="replace-tabs">
                <xsl:with-param name="text" select="substring-after($text, '&#9;')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head/>
  <body>
    <p>This is an example.</p>
    <pre>Text before<span class="tabspan">  </span>text in the middle<span class="tabspan"> </span>text after</pre>
  </body>
</html>

To make this closer to your attempt:

<xsl:variable name="BodyText">
    <span class="tabspan">
        <xsl:text>&#9;</xsl:text>
    </span>
</xsl:variable>

<xsl:template match="xhtml:pre">
    <xsl:copy>
        <xsl:call-template name="replace">
            <xsl:with-param name="text" select="."/>
            <xsl:with-param name="searchString" select="'&#9;'"/>
            <xsl:with-param name="replacement" select="$BodyText"/>
        </xsl:call-template>
    </xsl:copy>
</xsl:template> 

<xsl:template name="replace">
    <xsl:param name="text"/>
    <xsl:param name="searchString"/>
    <xsl:param name="replacement"/>
    <xsl:choose>
        <xsl:when test="contains($text, $searchString)">
            <xsl:value-of select="substring-before($text, $searchString)"/>
            <xsl:copy-of select="$replacement"/>
            <xsl:call-template name="replace">
                <xsl:with-param name="text" select="substring-after($text, $searchString)"/>
                <xsl:with-param name="searchString" select="$searchString"/>
                <xsl:with-param name="replacement" select="$replacement"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Upvotes: 1

Related Questions