Re: [xsl] Turning css (font-weight:bold) attributes into tags <b>

Subject: Re: [xsl] Turning css (font-weight:bold) attributes into tags <b>
From: Abel Braaksma <>
Date: Tue, 13 Mar 2007 17:49:51 +0100
Rick Livesey wrote:
I realise that the 'disable-output-escaping' method is a hack, but since
my tags are conditional I'm not sure how to get around this but there
must be a better solution out there and as an XSLT beginner, I'm a bit
Can anyone point me in the right direction?

d-o-e is never a good idea, especially not when in XSLT 2.0. Tokenizing styles is, I believe, best achieved using a simple micro-pipeline. Hmm, I wanted to give you a simple enough example, but then I went on generalizing it a bit and it became rather complex. I'm not sure of your skill level, but I am happy to talk you through the following stylesheet if necessary. Here are some pointers:

This example template assumes $input with 'span' elements and only text therein, but it is build with expansion in mind, so it won't be too hard to make it work with nodes inside 'span'.

The idea I had was this: make one place and one place only where you provide your stylesheet with a mapping from CSS to XML (or HTML, whichever you prefer). The example is rather basic, but is should get you going. Just change the global $css-mapping variable to your liking and you are done (you can use the placeholder [value] in any attribute, see font-family). All you need to do is add the literal nodes there.

What it does is using your input span elements and some matching/micro-pipelining/tunneling/tokenizing/more micro-pipelining to create hierarchical XML from a tokenizable string (the css). The mode switching is not strictly necessary and perhaps rather expensive, but I find it clearer this way.


-- Abel Braaksma

(I am sure the mailer will mess up my stylesheet big time... I hope it'll be a bit readable when archived...)


<xsl:output indent="yes" />
<xsl:variable name="input">
<span style="font-weight:bold;font-family:times new roman;text-decoration:underline">Some text</span>
<span style="font-weight:normal;font-family:arial;text-decoration:none">Some other text</span>
<span style="font-weight:bolder;">Bolder text</span>
<xsl:variable name="css-mapping">
<css property="font-weight" value="bold"><b /></css>
<css property="font-weight" value="bolder"><b /></css>
<css property="font-family"><font name="[value]" /></css>
<css property="font-weight" value="normal"><span /></css>
<css property="text-decoration" value="none"><span /></css>
<css property="text-decoration" value="underline"><u /></css>
<xsl:template match="/">
<xsl:apply-templates select="$input/*" />
<xsl:template match="span">
<xsl:variable name="css">
<xsl:apply-templates select="@style" >
<xsl:with-param name="text" tunnel="yes" select="text()" />
<xsl:copy-of select="$css"></xsl:copy-of>
<!-- the core: dissecting the @style attribute -->
<xsl:template match="@style">
<xsl:variable name="flat-css">
<xsl:for-each select="tokenize(., ';')">
<!-- prop/value pair of css attribute -->
<xsl:variable name="property" select="tokenize(., ':')[1]" />
<xsl:variable name="value" select="tokenize(., ':')[2]" />
<!-- apply for valued selection -->
<xsl:apply-templates select="
[@property = $property]
[@value = $value]/*" mode="css" />
<!-- apply for fill-in selection -->
<xsl:apply-templates select="
[@property = $property]
[not(@value)]/*" mode="css" >
<xsl:with-param name="value"
select="tokenize(., ':')[2]"
tunnel="yes" />
<!-- go from flat css to hierarchical -->
<xsl:apply-templates select="$flat-css/*[1]" mode="flat-to-hier"/>
<!-- simple treewalker template to go from flat to hierarchical -->
<xsl:template match="*" mode="flat-to-hier">
<xsl:param name="text" tunnel="yes" />
<xsl:apply-templates select="following-sibling::*[1]" mode="#current"/>
<xsl:sequence select="if(following-sibling::*) then '' else $text" />
<xsl:template match="* | @*" mode="css">
<xsl:apply-templates select="* | @*" mode="#current" />
<xsl:template match="@*[. = '[value]']" mode="css">
<xsl:param name="value" tunnel="yes" select="''"/>
<xsl:attribute name="{name()}" select="$value" />

Current Thread