linking in xslt, xml links

Links

1. How to create a link from the stylesheet
2. How to compute attribute value from sub-element
3. Linking acronym to a glossary entry
4. Converting link to HTML anchor
5. Managing an alphabetical linked page header
6. Providing and numbering cross references.

1.

How to create a link from the stylesheet

Linda van den Brink

You can create an element in your xsl stylesheet: 

<xsl:element name="a">
<xsl:attribute 
	name="href">filename.htm</xsl:attribute>
<xsl:text>clickable text</xsl:text>
</xsl:element>
            

2.

How to compute attribute value from sub-element

Jon Smirl

Q expansion:
E.g. 
<entry>
      <key>foo</key>
      <description>A sample link</description>
    </entry>

I would like to create the following output using XT:

<a href=
  "http://www.frob.org/search?type=simple&key=foo">
       A sample link</a>


<xsl:template match="entry">
    <a href=
     "http://www.frob.org/search?type=simple&key={key}">
        <xsl:value-of select="description"/>
    </a>
</xsl:template>

If key includes embedded spaces you will have to use urlEncode.

<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Tranform"     
	version="1.0"
   xmlns:url="
http://www.jclark.com/xt/java/java.net.URLEncoder">

<xsl:template match="entry">
    <a
href="http://www.frob.org/search?type=simple&key=
                          {url:encode(string(key))}">
        <xsl:value-of select="description"/>
    </a>
</xsl:template>
            

3.

Linking acronym to a glossary entry

G. Ken Holman

Q:Expansion 
given

<?xml version="1.0"?>
<doc>
<p> blah blah blah <acronym>RNIB</acronym> blah blah blah
    </p>

<def>
<term>RNIB</term>
 <explanation>This means xxxx</explanation>
</def>


  </doc>

How to create a link between the use of the acronym in the p
element and the corresponding term element in the glossary?

Answer:

I think it would be fast and easy with key(), but that
isn't implemented yet in XT so my suggestion below is a
"slow" way.

<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     
	version="1.0">
<xsl:output method="xml"/>
<xsl:template match="p|def">
   <p><xsl:apply-templates/></p>
</xsl:template>

<xsl:template match="acronym">  
    <!--find term with same content as acro-->
   <a href="#{generate-id(//def/term[.=current()])}">
     <xsl:value-of select="."/>
   </a>
</xsl:template>

<xsl:template match="term">
   <a name="{generate-id(.)}">
      <b><xsl:value-of 
	select="."/></b>: </a>
</xsl:template>

</xsl:stylesheet>


Also, from David Carlisle, to use 
case insensitive comparison:
Pass your strings through


translate(
 ___,
 "qwertyuiopasdfghjklzxcvbnm",
 "QWERTYUIOPASDFGHJKLZXCVBNM")




            

4.

Converting link to HTML anchor

Mike Brown


> I have an xml like this:
> 
> <text>
>     <p>Text1</p>
>     <p>Text2 <link 
	href="link1">label1</link></p>
>     <p><link 
	href="link2">label2</link></p>
>     <p>Text3 <link 
	href="link3">label3</link> text4 <link
> href="link4">label4</link> text 5</p>
> </text>
> 
> For example I want from the above:
> 
> <p>
>     <p>Text1</p>
>     <p>Text2 <a 
	href="link1">label1</a></p>
>     <p><a 
	href="link2">label2</a></p>
>     <p>Text3 <a 
	href="link3">label3</a> text4 <a 
> href="link4">label4</a> text 5</p>
> </p>

Well, your nesting of <p>'s within <p>'s is invalid HTML, so I'll take the liberty of changing the enclosing one to a <div>. These 3 templates should handle any arbitrary nesting of text, p, and link elements in the source tree.

<xsl:template match="text">
  <div>
     <xsl:apply-templates/>
  </div>
</xsl:template>

<xsl:template match="p">
  <p>
    <xsl:apply-templates/>
  </p>
</xsl:template>

<xsl:template match="link">
  <a href="{@href}">
    <xsl:apply-templates/>
  </a>
</xsl:template>

To help understand why they work, remember there are
built-in templates:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

And know that <xsl:apply-templates/>, if no select attribute is given, will find the templates that best match each child of the current node, and will invoke them one by one.

5.

Managing an alphabetical linked page header

Jeni Tennison


> I am stil having a bit of trouble with the selective links problem.
> Bascially i would like to display the the alpahbet at the top of the
> page and have a link (#b for example) to a letter if a title starts
> with it.

The easiest way to do this, I think, is with a recursive template that goes through the letters of the alphabet one by one and on each recursion checks whether there's anything to link to, using the key that you've set up.

The recursive template needs to take a string as its only parameter: it's initialised to the uppercase alphabet:


<xsl:template name="alphabetLinks">
  <xsl:param name="alphabet" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  ...
</xsl:template>

We'll be recursing through this alphabet, using the first letter each time and passing the rest of the alphabet on to the next recursion. So the letter we're interested in is the first character in the value of the $alphabet parameter:

<xsl:template name="alphabetLinks">
  <xsl:param name="alphabet" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="letter" select="substring($alphabet, 1, 1)" />
  ...
</xsl:template>

And if there's anything left in the alphabet, we want to recurse on to the rest of the alphabet:

<xsl:template name="alphabetLinks">
  <xsl:param name="alphabet" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="letter" select="substring($alphabet, 1, 1)" />
  ...
  <xsl:variable name="rest" select="substring($alphabet, 2)" />
  <xsl:if test="$rest">
    <xsl:call-template name="alphabetLinks">
      <xsl:with-param name="alphabet" select="$rest" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

To actually create the output, we need to check whether there are any entries to link to: if there are, we make a link; if there aren't, we don't:

<xsl:template name="alphabetLinks">
  <xsl:param name="alphabet" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="letter" select="substring($alphabet, 1, 1)" />
  <xsl:choose>
    <xsl:when test="key('ve-by-firstoccurrence', $letter)">
      <a href="#{$letter}">
        <xsl:value-of select="$letter" />
      </a>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$letter" />
    </xsl:otherwise>
  </xsl:choose>
  <xsl:variable name="rest" select="substring($alphabet, 2)" />
  <xsl:if test="$rest">
    <xsl:call-template name="alphabetLinks">
      <xsl:with-param name="alphabet" select="$rest" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Just call this template at the place where you want the alphabet links to be inserted, with:

  <xsl:call-template name="alphabetLinks" />

I'll leave you to add the finishing touches such as any punctuation you want around the letters.

6.

Providing and numbering cross references.

David Carlisle

The way to get numbered cross refs to work is get the numbering of the item (figure, chapter, table, ...) to work

when making the cross ref, go to the node that is referenced (with for-each or apply-templates in a special mode)

) produce the number with the same code as in step 1.

In your case you have

  <xsl:number count="figure" level="any" from="mainfunc"
	format="1"/> 

for step 1 so for step 2 you want

<xsl:template match="xref">
    <fo:basic-link background-color="lightblue"
internal-destination="{@xrefid}">
    <xsl:text>Fig. </xsl:text>
    <xsl:for-each select="id(@xrefid)"/>

so at this point (assuming that you have a dtd and your figure ids are of type ID so id() function works.) you are at the figure, so now you can do

<xsl:number count="figure" level="any" from="mainfunc" format="1"/>

then just finish up

  </xsl:for-each>
</fo:basic-link>
</xsl:template>

If you haven't declared your ids to be of type id you can declare a key instead and use key() rather than id()

Without seeing a (small) input file I'm a bit confused as you have internal-destination="{@xrefid}" which implies that your source doc has ids that you can use in teh generated FO for cross referencing, but in teh figure itself <xsl:template match="figure"> and <xsl:template match="graphic"> you are not accessing any id attribute and using generate-id to put a machine generated if into the FO which won't have any relationship to the id used in your fo:basic-link.

I suspect you don't want id="{generate-id(.)}£ but instead id="{@id}" or whatever the attribute is called that stores the value that is used in the xrefid attribute of xref.

Later:


> Here are parts of relavent schema I'm using unless I read it wrong 
> figure does have ID

XSLT1 won't look at your schema so IDs declared in a schema don't count. so you'd better use keys,

Add

<xsl:key  name="ids" match="*[@id] use="@id"/>

to the top level of your stylesheet then use

<xsl:for-each select="key('ids',@xrefid)">

instead of id(@xrefid)

the for-each will iterate over exactly one node (your figure) then xsl:number will generate the right number, since it will be executed with the figure as the context node.

The OP, Norma, then offered a solution using keys A template for the xref, and one for the actual figure which was being referenced



    <xsl:key name="ids" match="*[@id]" use="@id"/>
    
    <xsl:template match="xref">
            <fo:basic-link background-color="lightblue"
    internal-destination="{@idref}">
                <xsl:for-each select="key('ids',@xrefid)">
                    <xsl:text>Fig. </xsl:text>
                        <xsl:number count="figure" level="any"
    from="mainfunc" format="1"/>
                </xsl:for-each>
            </fo:basic-link>
    </xsl:template> 
    
    <xsl:template match="figure">
      <fo:block id="{@id}" text-align="center" font-style="italic"
    keep-together.within-page="always">
        <xsl:apply-templates/>
      </fo:block>
    </xsl:template>