Summary
This is an XSL primer written for those interested in learning the XSL language.
The primer covers the basics, and includes some examples and tools to get you started.
Foreward
Prerequisites
This primer is targeted towards those who are interested in learning XSL.
Familiarity with XML will help, but is not necessary.
Some familiarity with HTML is necessary for most of the examples.
A Web-based framework is made available (refer: Downloads::Tools) to perform XSL transformations.
The XslTransformer (sample app that runs inside this Web page) will be referenced throughout this primer.
Tools
What this Article Covers
This article provides an introduction to XSL, and is targeted towards those who are unfamiliar with the basic grammar and syntax.
What this Article Does Not Cover
This article does not discuss the various XML parsers or XSL processors, or how to use XSL from within an application.
Where Do You Get Your Information?
This primer is derived from the W3C specifications for XSL 2.0.
See Appendix A for a complete list of links.
Index
- Introduction
- What is XSL?
- What is XSLT?
- What are Namespaces?
- What is XPath?
- What are Nodes?
- What is XFO?
- What about XSL 1.0?
- Stylesheet Structure
- Modules
- The Basic Stylesheet
- How XML and XSL Relate
- What is XML
- XML Basics
- Example XML Document
- How does XSLT Relate to XML?
- How does XPath Relate to XML?
- But What Does It All Mean, Basil?
- How XSL Works
- A Note About XML and XSL Processors
- XSL Processor Implementation
- Context and Focus
- Changing Focus with Constructors and Patterns
- A Note About Whitespace
- Working Inside a Context
- Predicate Expressions
- Node Sets
- Conclusion
- Appendix A
- Appendix B
- Standard
- Simplified
- Embedded
Modification History
| Author | Date | Description |
| Stephen W. Cote | 11/14/2008 | Updated samples to use Web-base demonstrations. |
| Stephen W. Cote | 03/30/2002 | Corrected some formatting. |
| Stephen W. Cote | 03/01/2002 | Expanded section on How XSL Works. |
| Stephen W. Cote | 02/28/2002 | Updated based on reader review |
| Stephen W. Cote | 02/26/2002 | Document Created |
Introduction
What is XSL?
XSL is a language for expressing stylesheets and consists of XSLT, XPath, and XFO.
XSLT is a language for transforming XML, XPath is an expression language used to refer to parts of an XML document, and XFO is an XML vocabulary for specifying formatting semantics.
(from WC3 XSL Introduction).
XSL is implemented as a stylesheet.
An XSL stylesheet is called a stylesheet because one of its important roles is to add style to XML by changing the data to XFO, or a presentation format such as HTML.
An XSL stylesheet contains template rules.
A template can include patterns and content constructors.
Patterns are used to find data, and content constructors are used to output data.
So, what is XSL?
In the simplest terms, XSL is a way to find and output XML data.
What is XSLT?
XSLT transforms XML from the guise of a stylesheet.
An XSLT stylesheet is a valid XML file that conforms to the namespaces in XML.
What are Namespaces?
Namespaces are names used in XML to increase recognition, prevent collision, and that are identified by a URI reference.
A namespace is constructed of the namespace prefix, a colon, and the local name, and can be defined with the xmlns or xmlns: attributes.
(see Appendix A, Namespaces in XML).
The XSLT namespace is http://www.w3.org/1999/XSL/Transform.
Note that the 1999 is not the XSLT version.
Consider the following:
<cote:element xmlns:cote="http://www.whitefrost.com/xsl-schema/>
The element, element belongs to the namespace cote.
The cote namespace is declared in the xmlns attribute, and the attribute value specifies the URI for that namespace.
Since the xsl namespace is used to create XSLT stylesheets, you should become familiarized with the syntax of using xsl in the XSLT stylesheets.
What is XPath?
XPath is used to reference parts of an XML document and uses a path notation, like in a URI, to navigate through its hierarchical structure.
In the context of XSLT, XPath is used to find and test nodes.
What are Nodes?
Nodes are specific objects or elements that represent some form of data.
Consider the following:
<element attribute="test">child</element>
The previous example contains three nodes: an element node, element, an attribute node, attribute, and a text node, child.
What is XFO?
[ not covered ]
(refer: W3C Formatting Objects in XSL 1.0 Specification)
What about XSL 1.0?
Technical information for this document was derived from the later specifications.
A lot of what is discussed applies to both the 1.0 and 2.0 specifications.
Though a lot of the technical noise has been cut out, both specifications should be used when looking for specific implementation details.
Refer to Appendix A for links to these specifications.
[ top ]
Stylesheet Structure
The introduction included a brief summary of XSL.
Next, the basic structure of a stylesheet is discussed.
Modules
A stylesheet is made up of one or more stylesheet modules, where each module is a part of a well-formed XML document.
A standard module is an XML document that has xsl:stylesheet or xsl:transform as its root element; the xsl:stylesheet and xsl:transform elements include the same grammar.
An embedded module is like a standard module, except as the name implies, it is embedded inside an XML document.
The simplified module is a variation of the standard module where only a single template rule matches the root node, /.
Refer to Appendix B for the W3C examples of these implementations.
Root nodes are the top-level elements in XML documents.
All valid and complete XML documents must have one, and only one, root node.
The xsl:stylesheet element must have a version attribute, indicating required XSLT version.
So far, the following points have been asserted about XSLT stylesheets.
- They are valid XML.
- Standard stylesheet modules must have a specific root element, stylesheet or transform, in the xsl namespace.
- The xsl:stylesheet element must have a version.
The Basic Stylesheet
Based upon the above information, and if some familiarity with XML exists, the basic structure for a standard stylesheet module could be identified.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
</xsl:stylesheet>
[ top ]
How XML and XSL Relate
The previous section discussed the stylesheet specification and what is required to create a valid stylesheet.
Enough information was provided to describe XSL, but what about XML, and how do the two relate?
What is XML
The W3C describes XML as follows: [XML] describes a class of data objects called XML documents and partially describes the behavior of computer programs which process them. XML is an application profile or restricted form of SGML, the Standard Generalized Markup Language [ISO 8879].
By construction, XML documents are conforming SGML documents.
A little too technical?
How about this description, in comparison with HTML:
- HTML is a language useful for displaying data.
- XML is a language useful for describing data.
For example, consider a bullet list.
In HTML, authors use the predefined HTML elements for constructing a bullet list, and specify the data for each bullet point.
But, what kind of data is it?
Why is it in a bullet list?
The XML language allows authors to describe the data using a meaningful syntax.
XSL can be used to create the HTML bullet list from the XML data, or alternately display the data in some other fashion.
XML Basics
The basics of XML are:
- It should begin with a processing instruction representing the XML Declaration.
- It must have one, and only one, root.
- Elements must be closed, and case respected.
- Attributes must be quoted and have values.
Example XML Document
XML documents are logical structures that must conform to a set of rules in order to be valid (see XML Basics).
In other words, as long as you follow the grammatical rules, you can use your own descriptive elements.
The following is an example XML document, with the processing instruction at the top, a root node called my-document,
and one child node called neato. Notice that the shorthand style is used to close the neato element, rather than spelling out <neato></neato>.
<?xml version="1.0" encoding="utf-8" ?>
<my-document>
<neato />
</my-document>
Try it out now to test whether the XML syntax is valid.
- Click this link to open the XslTransformer tool.
- Click the Test button.
- The XML result should be true and the XSL result should be false.
How does XSLT Relate to XML?
Remember that XSLT is a stylesheet used to transform XML, and how XSLT has its own namespace and URI?
The namespace specifies the grammar that can be used within the stylesheet.
Now, remember that primary grammar used in stylesheets are template rules, and that template rules are used to find and output data.
By using these rules, the data described in the XML Document can be accessed by XSL.
The syntax for finding the XML data is specified for XPath.
How does XPath Relate to XML?
XPath is used from within an XSL stylesheet, and has a path-like structure (think directory paths on your computer).
For example, the XPath syntax for the neato element in the previous XML sample is: /my-document/neato.
But What Does It All Mean, Basil?
An overly simplified explanation: XSL is a sequential process that operates on XML data through XSLT, XPath, and XFO.
XSL defines a stylesheet that contains XSLT templates, which reference XML by using XPath.
Using the previous XML example, XSL could be used to look up the neato element, and output <b>Neato!</b>, if one were so inclined.
[ top ]
How XSL Works
In the previous section, the basic relationship between XML and XSL was explored.
By now, it should be clear that XSL operates in some fashion on XML by using XSLT, XPath, and XFO.
This section takes a closer look at what happens when XSL is used to transform XML.
A Note About XML and XSL Processors
Much like a Web page which uses an HTML parser, XML and XSL need a different kind of parser.
These parsers are preferably referred to as processors, usually one processor for XML and one for XSL.
For example, Microsoft's processor, MSXML, includes both, where Apache's are separate.
Nothing is going to happen without an XML and an XSL processor.
The first parses the XML file, the second parses the XSL file and processes the parsed XML with the parsed XSL.
For example,
the org.cote.js.xml library uses the XSLTProcessor and MSXML processors.
There are now two contexts to keep in mind: the source XML and XSL, which is the text, and the parsed XML and parsed XSL, which is what the source becomes when loaded by the processor.
From here on, references to parsed XML or parsed XSL imply that either has been loaded by the appropriate processor.
XSL Processor Implementation
An XML parser interprets an XML file to create an in-memory representation of the document.
There is a lot more to it than just a parser, hence the use of the name processor.
Since an XSL document is a valid XML document, there is no need for a separate XSL parser.
Parsed XSL is interpreted by an XSL Processor, which in turn resolves the markup for XSLT, XPath, and XFO.
The parsed XSL uses any content constructors or patterns to find and output data from the parsed XML document.
[ top ]
Context and Focus
For the most part, transforming XML with XSL is sequential from within stylesheet, not the XML (there are exceptions).
Remember that stylesheet templates contain content constructors and patterns, and so the sequence follows the constructors and patterns.
This is not consecutive from top to bottom, and it is possible to include some pretty robust logic by using constructors and patterns.
When a constructor is evaluated, the processor keeps track of the nodes being processed, called the focus.
The focus consists of the following five values: the context item, the context position, the context size, the context item, and the context document (from W3C XSLT 2.0 Spec).
So, at any given time while traversing the parsed XSL (remember, in the processor), a single node or a sequence of nodes is the focus.
It is important to keep the notion of the context in mind because it is easy to get lost when working within larger stylesheets.
In an XML document, the root node is the top-most element, not counting any special instructions.
Since an XSL stylesheet is an XML document, the same applies.
But, in the context of a parsed XSL, the intiation context is not the XML root node, but the parsed XML document node.
The document node is the parent of the XML root node.
The following example illustrates the starting context from the point of view of the parsed XML.
<!-- START CONTEXT -->
#document-context#
<!-- START TEXT -->
<?xml version="1.0" encoding="utf-8" ?>
<my-document>
<neato />
</my-document>
<!-- END XML -->
#/document-context#
The first context encountered by the parsed XSL acting upon parsed XML - here on referred as XSL process - is the document context.
The above example could also be represented figuratively as a series of patterns (XPath), as in the following example.
<!-- document root context -->
/
<!-- root node context -->
/my-document
<!-- node context -->
/my-document/neato
The context, or focus, is moved by using content constructors and patterns (XPath).
Changing Focus with Constructors and Patterns
One type of content constructor is xsl:template, which specifies a template rule.
Template rules, as mentioned previously, produce a sequence of nodes.
The match attribute of the xsl:template can be used to specify patterns.
Another type of content constructor is xsl:apply-templates, can process all child nodes of the context node, or change the focus to another node.
The following example demonstrates a very basic stylesheet that uses one content constructor, xsl:template, to match the root node.
At this point, it should be pointed out that anything that is not a part of the XSL namespace is treated as static output.
If you were to transform the previous XML example with the following XSL stylesheet example, what would you expect to see for the results?
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration = "yes" />
<!-- Focus is on the document node -->
<xsl:template match="my-document">
<!--
If the root node is <my-document /> this constructor's pattern
match will be successful, and the focus will shift to my-document,
but only within this template.
-->
Root!
</xsl:template>
</xsl:stylesheet>
Try it out now to see the transformation results.
- Click this link to open the XslTransformer tool.
- Click the Transform button.
A Note About Whitespace
If you transformed the previous example, you should have seen something like [return][space][space][space]Root![return].
Where did the extra spaces and line returns come from?
Those are called whitespace.
Remember, anything that is not defined by the xsl namespace is fair game, and that includes line returns, tabs, and spaces surrounding text nodes!
Whitespace is preserved if any of the following are true:
- The parent node is a whitespace-preserving element. For stylesheets, the set of whitespace-preserving elements is only xsl:text.
- The text node contains at least one non-whitespace character.
- An ancestor has the xmlns:space attribute set to preserve, and no closer parent has the attribute set to default.
So, the transformation of the above XSL process yields the whitespace because it is included as a part of the text node, Root!, which is really [return][space][space][space]Root![return]
XSL specifies several ways of dealing with whitespace, including the xsl:strip-space element and the aforementioned xml:space attribute.
However, these will not strip the whitespace from the text node.
A better syntax follows:
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration = "yes" />
<xsl:template match="my-document">
<xsl:text>Root!</xsl:text>
</xsl:template>
</xsl:stylesheet>
Try it out now to see how the whitespace changes.
The previous stylesheet would not return any whitespace because the text node that contains Root! contains no whitespace itself, and the default behavior of XSL is to strip whitespace not apart of text nodes.
This is an important distinction: whitespace can be stripped if it is not a part of a text node.
A text node is one or more characters that include non-whitespace characters.
Working Inside a Context
The previous sections discussed context, focus, and made a brief segue into whitespace.
Focus can be obtained for most types of nodes, and attributes.
When working with the context node, your can leverage the focus to obtain data from that node.
Consider the following XML document:
<?xml version="1.0" encoding="utf-8" ?>
<the-pond>
<title value="Local Pondlife" />
<description>The pond contains some ducks.</description>
<ducks>
<ducklings>
<desc>Random sampling of ducklings</desc>
<duckling>
<desc>A cute duck.</desc>
<name>Robert</name>
<color>Yellow</color>
<age units="days" value="8" />
<fuzziness value="3" />
</duckling>
<duckling>
<desc>A happy ducky.</desc>
<name>Sylvia</name>
<color>White</color>
<age units="days" value="9" />
<fuzziness value="1" />
</duckling>
<duckling>
<desc>A genious duck.</desc>
<name>Bo</name>
<color>Green</color>
<age units="days" value="3" />
<fuzziness value="2" />
</duckling>
</ducklings>
</ducks>
</the-pond>
The pattern representation for part of the above XML follows.
The '@' is used to denote an attribute, and is used in pattern matching.
The '#text' is used to denote a text node for this example.
Values for text and attribute nodes follow
/the-pond
/the-pond/title
/the-pond/title/@value = "Local Pondlife"
/the-pond/ducks
/the-pond/ducks/ducklings
/the-pond/ducks/duckling
/the-pond/ducks/duckling/desc
/the-pond/ducks/duckling/desc/#text = "A cute duck."
/the-pond/ducks/duckling/name
/the-pond/ducks/duckling/name/#text = "Robert"
/the-pond/ducks/duckling/color
/the-pond/ducks/duckling/color/#text = "Yellow"
/the-pond/ducks/duckling/age
/the-pond/ducks/duckling/age/@units = "days"
/the-pond/ducks/duckling/age/@value = "8"
/the-pond/ducks/duckling/fuzziness
/the-pond/ducks/duckling/fuzziness/@value = "3"
/the-pond/ducks/duckling
/the-pond/ducks/duckling
In terms of shifting focus, the above representation would change depending on the context.
For example, if the context node is /the-pond/ducks/ducklings then the pattern to match a duckling node would simply be duckling.
In that same context, the pattern to match the value attribute for of an age node would be duckling/age/@value
Note that in this example, these expressions would return a set of nodes.
When using patterns, a Predicate Expression (see Predicate Expressions section below; refer W3C Xpath) can be used to filter or test a node or node set.
A predicate expression exists within square brackets following a node.
For example, in the context of /the-pond/ducks/ducklings, the pattern to match ducks with a fuzziness value of 3 would be duckling[fuziness/@value = 3].
Time to introduce another content constructor, xsl:value-of.
This constructor, when used with the select attribute whose value is a pattern, can be used to extract data from a specific node, or without the select attribute, the context node.
Based upon what has been discussed so far, and using the xsl:value-of constructor, it should be straight forward to implement a very basic stylesheet to transform parts of the XML to HTML using XSL.
Consider the following XSL:
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- Match the root node -->
<xsl:template match="the-pond" xml:space="preserve">
<!-- Some basic HTML -->
<!-- Nodes not a part of the xsl namespace are result nodes -->
<html>
<head>
<!-- use xsl:value-of and select the @value attribute node -->
<title><xsl:value-of select="title/@value" /></title>
</head>
<body>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Try it out now to see the results.
In the above example, the pattern title/@value works because the context is /the-pond.
The pattern representing the context node, ./, is implied (meaning you don't have to use it).
So, title/@value is the same as ./title/@value, which is the same as /the-pond/title/@value.
Note that using the last example where the context node is part of the path (the-pond) is a waste of processor time because the processor will search for /the-pond again.
Also note that the xml:space attribute is being used to preserve white space around result elements (but not text nodes!).
Predicate Expressions
Predicate expressions are used within patterns to filter for specific nodes.
The syntax of a predicate expression is an opening square bracket, an expression, and a closing square bracket.
There are a variety of things that can be contained within the expression, such as tests for node existence and tests for node values, as well as some predefined functions.
An example of a predicate expression testing for the existence of a node is [title/@value].
This example would test if the value attribute existed on the title node of the current context node.
Alternately, this could be expressed as title[@value].
Predicate expressions are useful with the content constructors xsl:if and xsl:choose.
Suppose it was desirable to output the HTML title only if the title/@value existed in the XML.
Using xsl:if, the test attribute, and a predicate expression, the modified XSL would look like the following:
<!-- context node is /the-pond -->
<xsl:if test="title[@value]">
<title><xsl:value-of select="title/@value" /></title>
</xsl:if>
It is possible to output alternate HTML using xsl:choose and the xsl:when and xsl:otherwise constructors.
<!-- context node is /the-pond -->
<xsl:choose>
<xsl:when test="title[@value]">
<title><xsl:value-of select="title/@value" /></title>
</xsl:when>
<xsl:otherwise>
<title>NO DOCUMENT TITLE</title>
</xsl:otherwise>
</xsl:choose>
Node Sets
In the previous sections, a couple new content constructors and predicate expressions were introduced.
So far, everything has applied towards a single node; either the root node or the value attribute node on the title.
Now, a couple more content constructors are going to be introduced which will be used to obtain the duck data from the sample XML.
The xsl:apply-templates constructor, with the select attribute is used to shift focus to a set of nodes.
The matching part is easy: it's another xsl:template
The xsl:for-each constructor is used to temporarily shift focus to a set of nodes, like xsl:apply-templates, but the context is restricted to the selection.
The xsl:sort constructor can be used as an immediate child of xsl:for-each to sort the new node set.
In short, the first two constructors let you find data, and the third constructor is used to sort the results of the second.
For example, to create a list of all the duck names, the following XSLT could be used.
Three XPath functions are used in the following XSLT: text(), position(), and last().
In this example, the text() function has the same meaning as . or ./.
The '.' represents the current context node.
The position() function returns the current position in the node set, and the last() function returns the last node position in the set.
<!-- context node is /the-pond -->
<xsl:for-each
select="ducks/ducklings/duckling/name"
xml:space="default"
>
<xsl:sort select="text()" />
<xsl:value-of select="text()" />
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
Here is the explanation for what occurs in the previous XSLT.
The xsl:for-each constructor creates a node set of name nodes.
In the source XML, each name node contains the text of a duckling name.
While in the for-each loop, the context is one of the matching name nodes.
Note that the xsl:space attribute was switched back to default, which is necessary if used within the original XSL example.
The xsl:sort constructor is a special instruction to the xsl:for-each loop to sort the node set by the select attribute value.
In this case, the text of the name node.
The xsl:sort must be the first child element, if it is used.
The xsl:value-of constructor creates a result value out of the text node value of the name node.
The xsl:if constructor tests whether or not the node is the last is the result set, and if not, adds a comma and a space.
The result: Bo, Robert, Sylvia.
A more detailed example is to fully switch the focus over to a result set for each duckling node.
In this manner, a specific template can be defined for a duckling node.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="the-pond" xml:space="preserve">
<html>
<head>
<title><xsl:value-of select="./title/@value" /></title>
</head>
<body>
<table>
<tr>
<td>Name</td>
<td>Color</td>
<td>Age</td>
<td>Fuzziness</td>
<td>Description</td>
</tr>
<xsl:apply-templates select="ducks/ducklings/duckling">
<xsl:sort select="name/text()" />
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="duckling">
<tr>
<td><xsl:value-of select="name/text()" /></td>
<td><xsl:value-of select="color/text()" /></td>
<td><xsl:value-of select="age/@value" />
<xsl:value-of select="age/@units" />
</td>
<td>
<xsl:choose>
<xsl:when test="fuzziness[@value = 3]">Very fuzzy</xsl:when>
<xsl:when test="fuzziness[@value = 2]">Somewhat fuzzy</xsl:when>
<xsl:when test="fuzziness[@value = 1]">Not fuzzy at all</xsl:when>
<xsl:otherwise>Bald</xsl:otherwise>
</xsl:choose>
</td>
<td><xsl:value-of select="desc/text()" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Try it out now to see the results.
In the previous example, two xsl:template constructors are defined.
The first matches the root node, the-pond, and the second matches any duckling node.
In the first template, a basic HTML skeleton is outlined, including a table and a single table row containing column titles.
In the middle of the table is the xsl:apply-templates constructor, which shifts the context focus to a node set of duckling nodes.
When the duckling nodes are selected, the template that specifies a matche for those nodes is used.
Also note how the xsl:sort constructor was used within the xsl:apply-templates constructor to sort the node set result.
The second template applies to specific duckling nodes.
This template includes the HTML for a table row and cells as a result value.
The first, second, third, and last table cells retrive node values.
The fourth outputs static text based on a node value.
It is also very easy to provide summaries based on the XML, such as which duckling is the oldest, which is the fuzziest, and so on.
When you transform the XML with the above XSL stylesheet, a simple HTML page containing the duckling statistics is the result.
If combined with the previous XSL examples, it can be taken much further.
[ top ]
Conclusion
This primer covered some of the basics of XSL.
There is a lot more detail than what was provided here, but hopefully you're warmed up to more advanced XSL topics.
If you started out knowing very little about XSL and understood everything, great.
If not, let me know what confused you.
One final question: Do you feel extensible, punk? Well, do ya?
[ top ]
Appendix A
References
[ top ]
Appendix B
Standard, Simplified, and Embedded Stylesheets
Standard
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:template match="/">
<html>
<head>
<title>Expense Report Summary</title>
</head>
<body>
<p>Total Amount:
<xsl:value-of select="expense-report/total"/>
</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Simplified
<html xsl:version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Expense Report Summary</title>
</head>
<body>
<p>Total Amount: <xsl:value-of select="expense-report/total"/></p>
</body>
</html>
Embedded
<?xml-stylesheet type="text/xml" href="#style1"?>
<!DOCTYPE doc SYSTEM "doc.dtd">
<doc>
<head>
<xsl:stylesheet id="style1"
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="doc.xsl"/>
<xsl:template match="id('foo')">
<fo:block font-weight="bold"><xsl:apply-templates/></fo:block>
</xsl:template>
<xsl:template match="xsl:stylesheet">
<!-- ignore -->
</xsl:template>
</xsl:stylesheet>
</head>
<body>
<para id="foo">
...
</para>
</body>
</doc>
[ top ]