One of the beauties of a template-driven language like XSLT is that you can add to the collection of templates. Your new templates follow the same rules of selection as the stock templates, so your templates participate on an equal footing with the originals.
A new template is an
xsl:template element with a
match attribute that you add to your customization layer. The
match attribute specifies an XSL pattern that selects which elements it should be applied to. See the section “Template selection rules” if your template is not getting used as expected.
Here are some reasons why you might want to add new templates:
You want to specify different behavior for an element based on different values of an attribute such as
You have extended the DocBook schema to add elements or attributes that need to be formatted.
You want to add extra steps to the processing of a given element.
The following sections provide guidelines and examples.
role attribute is often used to distinguish one use of an element from another. If you want the difference expressed in formatting, you can add a template that responds to an attribute value. The following is an example of a template that draws a border around a
para that has
<xsl:template match="para[@role = 'intro']"> <fo:block border="0.5pt solid blue" padding="3pt" xsl:use-attribute-sets="normal.para.spacing"> <xsl:call-template name="anchor"/> <xsl:apply-templates/> </fo:block> </xsl:template>
In this example, the original template matching on
para was copied from
fo/block.xsl to the customization layer and then changed. The match attribute with
@role='intro' means that this template is applied only to
para elements with that
role attribute value. The template adds
padding properties for format such paragraphs.
This process is similar to replacing a template, but in this case the original template matching on plain
para was not changed. This template is new, and applies only to the special paragraphs.
This second example applies different formatting for different values of
<xsl:template match="para[@role]"> <fo:block padding="3pt" xsl:use-attribute-sets="normal.para.spacing"> <xsl:choose> <xsl:when test="@role = 'intro'"> <xsl:attribute name="border">0.5pt solid blue</xsl:attribute> </xsl:when> <xsl:when test="@role = 'concept'"> <xsl:attribute name="border">1pt solid black</xsl:attribute> <xsl:attribute name="margin-left">14pt</xsl:attribute> <xsl:attribute name="background-color">#EFEFEF</xsl:attribute> </xsl:when> ... </xsl:choose> <xsl:call-template name="anchor"/> <xsl:apply-templates/> </fo:block> </xsl:template>
Now the match is on any
para that has any
role attribute. The
xsl:choose statement tests the role value and applies attributes for each value. You can use
xsl:attribute to add an attribute to the parent block, as long as no output has been generated for the block yet. Unfortunately, XSL does not permit applying an attribute-set within an
If you want to add some steps to an existing template, you may be able to avoid copying and editing the whole template. Instead, you can use
xsl:apply-imports to call the original template, and put that in a new template that adds new steps.
For example, if you wanted to add an icon in the margin for each
figure element, you can create a new template that matches on
figure, generates the icon, and then applies the original figure template.
<xsl:template match="figure"> <xsl:call-template name="floater"> <xsl:with-param name="content"> <fo:external-graphic src="url(figure-icon.png)"/> </xsl:with-param> <xsl:with-param name="position">left</xsl:with-param> <xsl:with-param name="width">4pc</xsl:with-param> </xsl:call-template> <xsl:apply-imports/> </xsl:template>
match attribute matches on all
figure elements. Because it is in a customization layer that imports the DocBook XSL stylesheet, it has higher import precedence than the original template that matches on figure. So this template will be used instead of the original. It calls a utility template in the DocBook stylesheet named
floater that generates an
fo:float in the output. After that,
xsl:apply-imports is used to apply the original figure template to output the figure title and graphic.
If you customize the DocBook schema to add new elements, then you must customize the stylesheet to handle the new element names. If that is not done, then you will see error messages like the following when you process a document with such elements:
parentbut no template matches
If a new element is to be formatted the same as an existing element, you can probably copy and change the original template for the existing element so it handles the new element too. For example, if you add an element named
concept whose content model is just like
para, then you can copy the template with
match="para" to your customization layer and change the match attribute:
<xsl:template match="para | concept"> <fo:block xsl:use-attribute-sets="normal.para.spacing"> <xsl:call-template name="anchor"/> <xsl:apply-templates/> </fo:block> </xsl:template>
The only change here is to match on either
concept. Both are handled like paragraphs.
If a new element is similar to but not the same format as an existing element, then you have a decision to make. You could share a customized version of the existing template as in the previous example, and use
xsl:choose to handle any differences within the template. Or you could copy the original template, change it to match on only the new element name, and customize it as needed. Which method you choose depends on how different they need to be, and how hard they will be to maintain. If you want to apply a new attribute-set, it is usually easier to dedicate a new template to the element, like the following:
<xsl:template match="concept"> <fo:block xsl:use-attribute-sets="concept.properties"> <xsl:call-template name="anchor"/> <xsl:apply-templates/> </fo:block> </xsl:template>
If a new element is not similar to any existing DocBook element, you will need to write an entirely new template to handle it. In that case, it is best to study some of the existing DocBook templates to discover the numerous named utility templates, gentext features, and general methods used for handling DocBook content. A good XSLT reference is a must.
Once you have created a template matching on your new element, you have to make sure it gets used. Generally that is not a problem, because most (but not all) templates in DocBook XSL use a general
<xsl:apply-templates/> to process all their children. If the parent element of your new element includes such, then your new template will get used because it is the only one that matches on the new element.
But some parent elements are selective in what elements they process. For example, the following template for
simplelist selects only
member elements to process:
<xsl:template match="simplelist[@type='inline']"> ... <fo:inline> <xsl:for-each select="member"> ... </xsl:for-each> </fo:inline> </xsl:template>
If you were to create a new element that is a child of
simplelist, this its matching template would never be called in this context, because the new element name does not match
member. You would need to customize this template to process your new element.
The worst case in the stylesheets of this kind of selective processing is the handling of Next and Previous in chunked HTML. See for example the template named
html/chunk-code.xsl. Those templates use long lists of element names in
select attributes to figure out the next chunk.
Many elements are processed in more than one XSL mode, so you'll need to also add templates for those modes if appropriate. For example, if you want an element to appear in the table of contents, you may need a
mode="toc" template, and check the parent element that would call your new template too. If you want to cross reference to an element, then you need a
mode="xref-to" template. An element with title might need a
If any of your new elements is to generate text labels or titles, then you will want to add gentext elements to the
local.l10n.xml parameter (it is a parameter, not an XML file). You might need to add a general
l:gentext element, as well as
l:template elements in the contexts of
See the section “Generated text” for more information.
In order for your new templates to be used, it helps to understand the rules by which a template is selected to handle a given element. The rules are summarized here, from lowest to highest importance.
Assigned priority. Every template has an implicit priority number assigned to it based on how specifically it matches. For example, a
match="para[@role]" is more specific than
match="para", and so the first has a higher assigned priority. Assigned priorities are always in the range -0.5 to +0.5. If more than one template in a stylesheet file matches on the same element, the template with the highest priority is used. It is possible that two templates with different
match attributes resolve to the same assigned priority value, so you may need to add an explicit
priority attribute to resolve the conflict.
Explicit priority attribute. You can add a
priority attribute to any template to override the implicit priority assigned to it. Setting
priority="1", for example, will establish a higher priority than any assigned priority, since the latter are limited to the range -0.5 to +0.5.
Import precedence. If two templates match on the same element and one is imported, then the template in the importing stylesheet takes precedence, regardless of the relative priority values (assigned or explicit). The local template is said to have a higher import precedence than the imported template. This it the key feature that makes customization layers work. When you import the DocBook stylesheet, you only have to change the templates you need to change, and your templates always have higher import precedence than the originals.
Keep in mind that import precedence always wins over priority values. That can sometimes lead to surprising results. For example, if a template with a
priority="10" attribute is imported, and the importing stylesheet has a match on that element but no
priority attribute, then the high priority value on the imported template counts for nothing, because the higher import precedence of the local template wins. Likewise with assigned priorities. An imported template with a very specific match will not be used if any template in the importing stylesheet matches on the same element, even if it is a looser match.
See the section “Import precedence” for more information and examples of import precedence.
|DocBook XSL: The Complete Guide - 4th Edition||PDF version available|
Copyright © 2002-2007 Sagehill Enterprises