XML images, graphics in xslt

Images

1. Displaying images
2. Pre-loading images
3. Problem generating dynamic namespace declarations

1.

Displaying images

Mike Brown



> I need to display an image based on the filename from the XML 
> file.  I understand that the result tree simply needs to show 
> the html needed to display the file (that being <img 
> src="picture.jpg">)

No, the result tree is a node tree (an abstract thing that exists only as some kind of a data structure in the XSL processor). XSLT conveniently lets you say how you want that node tree to be created via a syntax that looks like XHTML. This syntax is described in the XSLT spec as "literal result elements". So for example in your stylesheet you can have

<img src="{foo}"/>

This tells the XSL processor to add an element named 'img' to the result tree, with an attribute named 'src' and a value of the result of evaluating the XPath expression foo. The XPath expression should be in curly braces and should return a string or a node that you want to see the string value of. String values for each node type are explained in the XPath spec.

If your source XML is like this:

<mydata>
  <ImageInfo>
    <Image ID="123" URI="picture.jpg"/>
  </ImageInfo>
</mydata>

And your context node was the 'ImageInfo' element, as in the case when you match on it, the XPath expression to get the 'URI' attribute of the 'Image' element that is a child of the context node is quite simply Image/@URI, which you'd put in the curly braces:

<xsl:template match="ImageInfo">
  <img src="{Image/@URI}"/>
</xsl:template>

If you have the XSL processor serialize and emit the result tree as XML, you'll see in the output

<img src="picture.jpg"/>

And if you have it serialize it as HTML, you'll see

<img src="picture.jpg">

2.

Pre-loading images

Mike Kay


> Given the XML:
> 
> <?xml version="1.0"?>
> <LeftNavList>
> 	<PreLoadImage>images/image1</PreLoadImage>
> 	<PreLoadImage>images/image2</PreLoadImage>
> 	<PreLoadImage>images/image3</PreLoadImage>
> </LeftNavList>
> 
> I would like to generate the following html snippet:
> 
> <body bgcolor="#ffffff" onLoad="PreloadImages
> ('images/image1.gif','images/image2.gif','images/image3.gif');">
> 

  

<xsl:template match="LeftNavList">
  <body bgcolor="#ffffff">
    <xsl:attribute name="onLoad">PreloadImages(
       <xsl:for-each select="PreLoadImage">
         '<xsl:value-of select=".">.gif'
         <xsl:if test="position()!=last()">,</xsl:if>
       </xsl:for-each>);</xsl:attribute>
  </body>
</xsl:template>
  

You may want to adjust that a bit for whitespace.

3.

Problem generating dynamic namespace declarations

David Carlisle

With saxon, I think that generating a namespace node in the result based on a variable value you need to do something like

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"
                xmlns:saxon="http://icl.com/saxon"
                   extension-element-prefixes="saxon"
                >

<xsl:output method="xml" indent="yes"/>

<xsl:param name="p" select="'foo'"/>
<xsl:param name="n" select="'http://x/y/z'"/>

<xsl:template match="/">
<xsl:variable name="x">
 <xsl:element name="{$p}:x" namespace="{$n}"/>
</xsl:variable>
<xxx path="{$p}:that">
  <xsl:for-each select="saxon:node-set($x)/*/namespace::*[name()=$p]">
  <xsl:copy/>
  </xsl:for-each>
<!--
  <xsl:copy-of select="saxon:node-set($x)/*/namespace::*"/>
- -->
</xxx>
</xsl:template>

</xsl:stylesheet>

applying this to anything (eg itself) gives you

<?xml version="1.0" encoding="utf-8" ?>
<xxx xmlns:foo="http://x/y/z" path="foo:that"/>

But this relies on some grey areas in the spec about copying namespace nodes, (which saxon, as Mike pointed out on this list some time back, interprets slightly differently for copy and copy-of.)

Mike Kay responds (July 2000)

Yes I know:-) But perhaps in version 1+x making the current saxon behaviour for copy the defined behaviour (for copy and copy-of) would probably be an improvement. Still it's a bit of a hack, especially as node-set's an extension. so an xsl:namespace-declaration that worked like xsl:attribute would probably be a good addition as well.

And David finishes with :-)

so, a version that only explictly copies element and attribute nodes.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"
                xmlns:saxon="http://icl.com/saxon"
                   extension-element-prefixes="saxon"
                >

<xsl:output method="xml" indent="yes"/>

<xsl:param name="p" select="'foo'"/>
<xsl:param name="n" select="'http://x/y/z'"/>

<xsl:template match="/">
<xsl:variable name="x">
 <xxx path="{$p}:that">
 <xsl:attribute name="{$p}:x" namespace="{$n}"/>
 </xxx>
</xsl:variable>
  <xsl:for-each select="saxon:node-set($x)/*">
  <xsl:copy>
   <xsl:copy-of select="@*[local-name()!='x']"/>
  </xsl:copy>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
bash-2.01$ saxon ns3.xsl ns3.xsl
<?xml version="1.0" encoding="utf-8" ?>
<xxx xmlns:foo="http://x/y/z" path="foo:that"/>

as it doesn't use namespace:: this also works with xt (once you change the extension namespace)