How to transform XHTML+CSS with embedded images to XSL-FO? - xslt

I'm trying to transform a simple HTML page to XSL-FO, to feed into Apache FOP for PDF rendering.
The steps are: HTML+CSS -> XHTML -> XSL-FO -> PDF.
I've used the java library CSSToXSLFO to transform XHTML to XSL-FO. This works, however it's incapable of handling embedded images.
Are there any tools to transform
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>hello</title>
</head>
<body>
<h1 style="color: green">Hello world!</h1>
<img src="data:image/png;base64,iVBORw...=" />
</body>
</html>
into
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:block color="green">Hello world!</fo:block>
<fo:external-graphic src="url(data:image/png;base64,iVBORw...=)" content-height="scale-to-fit" content-width="scale-to-fit" scaling="uniform"/>
</fo:block>
</fo:flow>
?

If the FOP processor supports data URIs in fo:external-graphic you can of course use XSLT to transform XHTML to XSL-FO with e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xpath-default-namespace="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="sample">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="sample">
<xsl:apply-templates select="html/body"/>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="body">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:flow>
</xsl:template>
<xsl:template match="h1">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="img">
<fo:external-graphic src="{#src}" content-height="scale-to-fit" content-width="scale-to-fit" scaling="uniform"/>
</xsl:template>
</xsl:stylesheet>
That is a minimal example to handle the h1 and the img element, I haven't tried to spell out any HTML CSS style attribute to XSL-FO presentational attribute transformation but you can of course use e.g. <xsl:apply-templates select="#*, node()"/> instead of <xsl:apply-templates/> and then add templates to transform e.g. style="color: green" to color="green". As CSS has its own, non-XML syntax, obviously writing a full parser for arbitrary style attributes is a demanding task beyond the scope of StackOverflow answers.
I am also not quite sure about the allowed src attribute syntax in XSL-FO, FOP seems to understand the direct src="{#src}" just fine, but of course, to create the format you indicated in your question, you could as well use src="url({#src})".

Related

How to show correctly tabulation in xsl-fo?

I have an XML document and I am creating an XSL-FO file to convert it to pdf with apache-fop.
In the xml, there are sections <code> to show... code.
In the xsl-fo, I added the white-space="pre" sentence to preserve the code format, but tabulations are shown like single space:
XML section:
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
XSL-FO section:
<xsl:template match="code">
<fo:block font-size="10pt" font-family="monospace" white-space="pre" color="#008000" background-color="#f8f8f8">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
Resulting PDF:
function callback( widget )
{
var ui = widget; // It should be a tab
}
So my question is: How to preserve or set the size of the tabulation?
Edited: I am using apache-fop 1.1
A full example:
proyecto.xml (do not forget to replace the 4 spaces by a tab before "var ui...")
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="proyecto.xsl"?>
<document>
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
</document>
proyecto.xsl
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match ="document">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="OnePage" margin="1in"
page-height="29.7cm"
page-width="21cm"
margin-top="2.5cm"
margin-bottom="2.5cm"
margin-left="3.5cm"
margin-right="2.5cm">
<fo:region-body margin="0cm"/>
</fo:simple-page-master>
<fo:page-sequence-master master-name="Page">
<fo:single-page-master-reference master-reference="OnePage"/>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="Page">
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="code">
<fo:block font-size="10pt" font-family="monospace" white-space="pre">
<xsl:apply-templates/>
<!--<xsl:value-of select="replace( . , 'a', 'b')"/>-->
</fo:block>
</xsl:template>
</xsl:stylesheet>
PDF result (screen):
Replace the tabs by four spaces, as suggested by #mzjn already in the comments.
XML Input
<code><![CDATA[
function callback( widget )
{
var ui = widget; // It should be a tab
}
]]></code>
XSLT Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="A4-portrait"
page-height="29.7cm" page-width="21.0cm" margin="2cm">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4-portrait">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="10pt" font-family="monospace" linefeed-treatment="preserve" white-space-collapse="false" white-space-treatment="preserve" wrap-option="no-wrap" color="#008000" background-color="#f8f8f8">
<xsl:value-of select="replace(code,' ',' ')"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Now, after replacing the tab characters with whitespaces, the output is rendered correctly by FOP.
There is no obvious flaw with outputting tabs, but bear in mind that Apache FOP is only partially compliant in regard to the whitespace-treatment property: http://xmlgraphics.apache.org/fop/compliance.html , even if it says that only fo:inline elements are affected of this.

XSL Second-pass template failing when attribute is selected

I'm wondering if anybody can shed any light on why a particular XSL template is failing to do what is intended. I am generating XSL-FO using XSL 2.0 (Saxon HE). I want to invert certain font styles (so that italic text within an italic paragraph becomes roman, for instance). (In my actual XSL, I do this as the second part of a two-pass process. The issue occurs regardless, so my example shows a single pass for the purposes of simplicity.)
Sample input:
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="skeleton">
<fo:region-body margin="1in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="skeleton">
<fo:flow flow-name="xsl-region-body">
<fo:block><fo:inline font-style="italic">A Title <fo:inline color="black" font-style="italic">With Some Roman Text</fo:inline> in the Middle</fo:inline></fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
Sample XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//fo:inline[parent::*[#font-style=current()/#font-style]]/#font-style">
<xsl:message>Found nested font-style.</xsl:message>
<xsl:attribute name="font-style">normal</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
My desired result is:
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="skeleton">
<fo:region-body margin="1in" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="skeleton">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<fo:inline font-style="italic">
A Title
<fo:inline color="black" font-style="normal">
With Some Roman Text
</fo:inline>
in the Middle
</fo:inline>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
This is the result I get using this online tester: http://chris.photobooks.com/xml/default.htm
When I run this transform using Saxon the interior fo:inline remains italic and my debug message never appears.
I've found a workaround using for-each (if I do not attempt to select the attribute, but rather select the matching fo:inline, the template is triggered). But I'm curious to know what is wrong with this, in my opinion, much cleaner solution.
Thanks!
(Just to add a little more information, here's the less-ideal template that does get me the result I want, at least so far:
<xsl:template match="//fo:inline[parent::*[#font-style=(current()/#font-style)]]">
<xsl:message>Found nested font-style.</xsl:message>
<xsl:copy>
<xsl:for-each select="#*">
<xsl:message><xsl:value-of select="local-name()"/></xsl:message>
<xsl:choose>
<xsl:when test="local-name()='font-style'">
<xsl:attribute name="font-style">normal</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</xsl:copy>
It is possibly something to do with this line...
<xsl:template match="//fo:inline[parent::*[#font-style=current()/#font-style]]/#font-style">
In XSLT 1.0, I get an error...
The current() function may not be used in a match pattern.
I am not sure if the same happens in XSLT 2.0, but try changing the line to this, and see if that makes a difference
<xsl:template match="//fo:inline[#font-style = parent::*/#font-style]/#font-style">
I managed to obtain the result you are looking for changing the XPath expression to
//fo:inline[parent::*[#font-style]]/#font-style
The previous XPath expression matched no nodes in the document, so the template was never called (and so the message was also not printed).
Taking this to its logical extension, you probably want to convert to "normal" any fo:inline that has an odd number of ancestor fo:inline elements with the same font-style as this one (e.g. if you had italic within italic within italic then only the middle one of the three would want to be converted back to normal).
<xsl:template match="fo:inline[
count(ancestor::fo:inline[#font-style = current()/#font-style]) mod 2 = 1]">
This works because XSLT 2.0 defines current() in a pattern as the node that the pattern is being tested against (in this case the fo:inline that is the subject of the outer predicate).

xsl:sort: sorting by numeric value

I have to sort out the codes in numerical order.
The codes have four characters and four numerals.
for example,
COMP2100
COMP2400
COMP3410
LAWS2202
LAWS2250
when I just do <xsl:sort select="code" order="ascending" />
it displays above result.
However, I want that to be in 'numerical order' that is
COMP2100
LAWS2202
COMP2250
COMP2400
COMP3410
How do I do this?
Note: the OP has now provided sample XML. The below theories can be trivially adapted to this XML.
I. XSLT 1.0 (part 1)
Here is a simple solution that assumes your assertion ("the codes have four characters and four numerals") will always be the case:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="vNums" select="'1234567890'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<t>
<xsl:apply-templates>
<xsl:sort select="substring(., 5)"
data-type="number" />
</xsl:apply-templates>
</t>
</xsl:template>
</xsl:stylesheet>
...is applied to an imagined XML document, shuffled into random order:
<?xml version="1.0" encoding="utf-8"?>
<t>
<i>COMP3410</i>
<i>LAWS2202</i>
<i>COMP2400</i>
<i>COMP2100</i>
<i>LAWS2250</i>
</t>
...the correct result is produced:
<?xml version="1.0" encoding="utf-8"?>
<t>
<i>COMP2100</i>
<i>LAWS2202</i>
<i>LAWS2250</i>
<i>COMP2400</i>
<i>COMP3410</i>
</t>
Explanation:
The Identity Transform -- one of the (if not the) most fundamental design patterns in XSLT -- copies all nodes from the source XML document to the result XML document as-is.
One template overrides the Identity Transform by sorting all children of <t> based upon the characters in the string from position 5 to the string's end.
Again, note that this solution assumes your original assertion -- "the codes have four characters and four numerals" -- is (and always will be) true.
II. XSLT 1.0 (part 2)
A (potentially) safer solution would be to assume that there might be numerous non-numeric characters in various positions within the <i> nodes. In that case, this XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="vNums" select="'1234567890'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<t>
<xsl:apply-templates>
<xsl:sort select="translate(., translate(., $vNums, ''), '')"
data-type="number" />
</xsl:apply-templates>
</t>
</xsl:template>
</xsl:stylesheet>
...provides the same result:
<?xml version="1.0" encoding="utf-8"?>
<t>
<i>COMP2100</i>
<i>LAWS2202</i>
<i>LAWS2250</i>
<i>COMP2400</i>
<i>COMP3410</i>
</t>
Explanation:
The Identity Transform is once again used.
In this case, the additional template uses the so-called Double Translate Method (first proposed by Michael Kay and first shown to me by Dimitre Novatchev) to remove all non-numeric characters from the value of each <i> element before sorting.
III. XSLT 2.0 Solution
Here's a possible XSLT 2.0 solution is very similar to part 2 of the XSLT 1.0 solution; it merely replaces the Double Translate Method with XPath 2.0's ability to handle regular expressions:
<xsl:sort select="replace(., '[^\d]', '')" data-type="number" />
Note that by no means are you required to use regular expressions in XPath 2.0; the Double Translate Method works just as well as in XPath 1.0. The replace() method will, however, most likely be more efficient.
There are two obvious errors in the provided XSLT code:
The namespace used to select elements is different from the default namespace of the provided XML document. Just change: xmlns:xsi="file://Volumes/xxxxxxx/Assignment" to xmlns:xsi="file://Volumes/xxxxxxx/Assignment".
The sort at present is not numeric. Change:
<xsl:sort select="xsi:code" order="ascending" />
to:
<xsl:sort select="substring(xsi:code, 5)" data-type="number" />
The complete transformation becomes:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xsi="file://Volumes/u4783938/Assignment">
<xsl:template match="/">
<html>
<head>
<title> Course Catalogue </title>
</head>
<body bgcolor="#FF9999">
<h1> <div style="text-align:center"> Course Catalogue </div> </h1>
<xsl:for-each select="xsi:catalogue/xsi:course">
<xsl:sort select="substring(xsi:code, 5)"
data-type="number" />
<div style="width:1000px;margin-bottom:4px;color:white;background-color:#F36;text-align:justify;border:outset;margin-left:auto;margin-right:auto;">
<xsl:apply-templates select="xsi:code" />
<br />
<xsl:apply-templates select="xsi:title" />
<br />
<xsl:apply-templates select="xsi:year" />
<br />
<xsl:apply-templates select="xsi:science" />
<br />
<xsl:apply-templates select="xsi:area" />
<br />
<xsl:apply-templates select="xsi:subject" />
<br />
<xsl:apply-templates select="xsi:updated" />
<br />
<xsl:apply-templates select="xsi:unit" />
<br />
<xsl:apply-templates select="xsi:description" />
<br />
<xsl:apply-templates select="xsi:outcomes" />
<br />
<xsl:apply-templates select="xsi:incompatibility" />
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
and when applied on this XML document:
<catalogue xmlns="file://Volumes/u4783938/Assignment"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="file://Volumes/u4443554/Assignment/courses.xsd">
<course>
<code>ABCD3410</code>
<title> Information Technology in Electronic Commerce </title>
<year>later year</year>
<science>C</science>
<area> Research School of Computer Science </area>
<subject> Computer Science </subject>
<updated>2012-03-13T13:12:00</updated>
<unit>6</unit>
<description>Tce </description>
<outcomes>Up trCommerce. </outcomes>
<incompatibility>COMP1100</incompatibility>
</course>
<course>
<code>COMP2011</code>
<title> Course 2011 </title>
<year>Year 2011</year>
<science>C++</science>
<area> Research School of Computer Science </area>
<subject> Computer Science </subject>
<updated>2012-03-13T13:12:00</updated>
<unit>6</unit>
<description>Tce </description>
<outcomes>Up trCommerce. </outcomes>
<incompatibility>COMP1100</incompatibility>
</course>
</catalogue>
the produced result is now correctly sorted by the numeric part of the course code:
<html xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xsi="file://Volumes/u4783938/Assignment">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title> Course Catalogue </title>
</head>
<body bgcolor="#FF9999">
<h1>
<div style="text-align:center"> Course Catalogue </div>
</h1>
<div style="width:1000px;margin-bottom:4px;color:white;background-color:#F36;text-align:justify;border:outset;margin-left:auto;margin-right:auto;">COMP2011<br> Course 2011 <br>Year 2011<br>C++<br> Research School of Computer Science <br> Computer Science <br>2012-03-13T13:12:00<br>6<br>Tce <br>Up trCommerce. <br>COMP1100
</div>
<div style="width:1000px;margin-bottom:4px;color:white;background-color:#F36;text-align:justify;border:outset;margin-left:auto;margin-right:auto;">ABCD3410<br> Information Technology in Electronic Commerce <br>later year<br>C<br> Research School of Computer Science <br> Computer Science <br>2012-03-13T13:12:00<br>6<br>Tce <br>Up trCommerce. <br>COMP1100
</div>
</body>
</html>

Convert XSLT 1.0 to 2.0

I need help converting an XSLT 1.0 file to 2.0 (so I can use the XSLT 2.0 replace() function call).
I've Googled, searched different books and SO with no success. I tried changing version="1.0" to 2.0 and changing html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" to simply html but this all just results in XSLTProcessor errors.
Any help is greatly appreciated. Thanks in advance.
Here is my XSLT:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" encoding="ISO-8859-1" />
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<xsl:element name="meta">
<xsl:attribute name="name">description</xsl:attribute>
<xsl:attribute name="content"><xsl:value-of select="EventList/title"/></xsl:attribute>
</xsl:element>
<xsl:element name="link"><xsl:attribute name="rel">alternate</xsl:attribute><xsl:attribute name="type">application/rss+xml</xsl:attribute><xsl:attribute name="title">RSS</xsl:attribute><xsl:attribute name="href"><xsl:value-of select="EventList/rssURL"/></xsl:attribute></xsl:element>
</head>
<body>
<xsl:apply-templates select="EventList" />
</body>
</html>
</xsl:template>
<xsl:template match="EventList">
<xsl:choose>
<xsl:when test="Event">
<xsl:apply-templates select="Event"/>
</xsl:when>
<xsl:otherwise>
<div class="eventItem">
<div class="eventItemText">
<p>There are currently no events posted for this category.</p>
</div>
</div>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- List -->
<xsl:template match="Event">
<li>
<!-- Title -->
<xsl:if test="eventStatus = 2"><xsl:value-of select="eventStatusString"/> - </xsl:if>
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/e/?d=<xsl:value-of select="replace(detailpath,'/events/','')"/></xsl:attribute><xsl:attribute name="id"><xsl:value-of select="eventID"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="title" disable-output-escaping="yes"/>
</xsl:element>
<!-- Date and time -->
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/details.php?detailpath=<xsl:value-of select="detailpath"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:choose>
<xsl:when test="repeatRuleID > 0">
Ongoing <xsl:value-of select="repeatRuleText"/> from <xsl:value-of select="beginDate"/> through <xsl:value-of select="repeatUntilDate"/>.
<xsl:if test="repeatRuleID=99">See details for exact dates and times.</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="string(beginDay)"><xsl:value-of select="beginDay"/>, </xsl:if>
<xsl:value-of select="beginDate"/>.
</xsl:otherwise>
</xsl:choose>
<xsl:if test="repeatRuleID!=99">
<xsl:if test="string(beginTime)">
<xsl:text disable-output-escaping="yes"></xsl:text><xsl:value-of select="beginTime"/>.
</xsl:if>
</xsl:if>
</xsl:element>
<!-- Location -->
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/details.php?detailpath=<xsl:value-of select="detailpath"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="locationText"/>
</xsl:element>
</li>
</xsl:template>
</xsl:stylesheet>
In addition to changing the version attribute to '2.0', you need to feed your XSLT 2.0 code to an XSLT 2.0 processor.
At present, some of the XSLT 2.0 processors I use are:
Saxon 9.x
XQSharp 2.0
AltovaXML (XMLSpy)
Do note that an existing XSLT 1.0 code may behave differently under XSLT 2.0 -- the most obvious differences are that <xsl:value-of> no longer produces the string value of only the first node from a node-set, and that in XSLT 2.0 the dreaded RTF type has been eliminated, so no xxx:node-set() extension function is needed/provided.
You should also check the non normative XSLT 2.0 Appendix J Changes from XSLT 1.0 .
This section lists all known cases
where a stylesheet that was valid
(produced no errors) under XSLT 1.0,
and whose behavior was fully specified
by XSLT 1.0, will produce different
results under XSLT 2.0.
While you're converting the code, you should get rid of those ugly disable-output-escaping="yes" attributes. They are almost certainly not wanted. The use of this attribute usually indicates that it was written by a novice with a poor understanding of the language. This also applies to the use of verbose constructs like
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/e/?d=<xsl:value-of select="replace(detailpath,'/events/','')"/></xsl:attribute><xsl:attribute name="id"><xsl:value-of select="eventID"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="title" disable-output-escaping="yes"/>
</xsl:element>
that could be replaced by the much more readable
<a href="http://events.stanford.edu/e/e/?d={replace(detailpath,'/events/','')}"
id="{#eventID}" rel="external">
<xsl:value-of select="title"/>
</a>
Generally, some refactoring is long overdue for this code.

Where do I put an XSL function in an XSL document?

I have an XSL style sheet for which I need to add some custom string manipulation using an xsl:function. But I am having trouble trying to work out where to put the function in my document.
My XSL simplified looks like this,
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:my="myFunctions" xmlns:d7p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="Master.xslt"/>
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!-- starts actual layout -->
<fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body">
<!-- this defines a title level 1-->
<fo:block xsl:use-attribute-sets="heading">
HelloWorld
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
And I want to put in a simple function, say,
<xsl:function name="my:helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:function>
But I cannot work out where to put the function, when I put it under the node I get an error saying 'xsl:function' cannot be a child of the 'xsl:stylesheet' element., and if I put it under the node I get a similar error.
Where should I put the function? Idealy I would like to put my functions in an external file and import them into my xsl files.
There is no xsl:function in XSL version 1.0. You have to create a named template
<xsl:template name="helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:template>
(...)
<xsl:template match="something">
<xsl:call-template name="helloWorld"/>
</xsl:template>
You can upgrade the stylesheet version to 2.0
Then in stylesheet declaration specify as
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="http://www.**.com">
** Your choice you can specify anything as your wish
then below this specify your function
<xsl:function name="func:helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:function>
Then in template you can use it as
<xsl:template match="/">
<xsl:value-of select="func:helloWorld"/>
</xsl:template>