1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
|
<chapter>
<chapterinfo>
<releaseinfo role="meta">
$Id: publishing.xml,v 1.1 2001/04/02 13:03:43 nwalsh Exp $
</releaseinfo>
<author><surname>Stayton</surname>
<firstname>Bob</firstname></author>
<copyright><year>2000</year><holder>Bob Stayton</holder>
</copyright>
</chapterinfo>
<title>DocBook XSL</title>
<?dbhtml filename="publishing.html"?>
<sect1>
<title>Using XSL tools to publish DocBook
documents</title>
<para>There is a growing list of tools to process DocBook
documents using XSL stylesheets. Each tool implements parts
or all of the XSL standard, which actually has several
components:
<variablelist>
<varlistentry>
<term>Extensible Stylesheet Language (XSL)</term>
<listitem>
<para>A language for expressing stylesheets written
in XML. It includes the formatting object language, but
refers to separate documents for the transformation
language and the path language.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>XSL Transformation (XSLT)</term>
<listitem>
<para>The part of XSL for transforming XML documents
into other XML documents, HTML, or text. It can be used to
rearrange the content and generate new content.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>XML Path Language (XPath)</term>
<listitem>
<para>A language for addressing parts of an XML
document. It is used to find the parts of your document to
apply different styles to. All XSL processors use this
component.</para>
</listitem>
</varlistentry>
</variablelist></para>
<para>To publish HTML from your XML documents, you just
need an XSLT engine. To get to print, you need an XSLT
engine to produce formatting objects (FO), which then must
be processed with a formatting object processor to produce
PostScript or PDF output.</para>
<para>James Clark's XT was the first useful XSLT engine,
and it is still in wide use. It is written in Java, so it
runs on many platforms, and it is free (
<ulink url="http://www.jclark.com">http://www.jclark.com</ulink>).
XT comes with James Clark's nonvalidating parser XP, but
you can substitute a different Java parser. Here is a
simple example of using XT from the Unix command line to
produce HTML: You'll need to alter your
<parameter>CLASSPATH</parameter> environment variable to
include the path to where you put the
<filename>.jar</filename> files from the XT
distribution.</para>
<screen>CLASSPATH=xt.jar:xp.jar:sax.jar
export CLASSPATH
java com.jclark.xsl.sax.Driver <replaceable>filename.xml</replaceable> <replaceable>docbook/html/docbook.xsl</replaceable> > <replaceable>output.html</replaceable></screen>
<para>If you replace the HTML stylesheet with a
formatting object stylesheet, XT will produce a formatting
object file. Then you can convert that to PDF using FOP, a
formatting object processor available for free from the
Apache XML Project (
<ulink url="http://xml.apache.org">http://xml.apache.org</ulink>).
Here is an example of that two stage processing:</para>
<screen>CLASSPATH=xt.jar:xp.jar:sax.jar:fop.jar
export CLASSPATH
java com.jclark.xsl.sax.Driver <replaceable>filename.xml</replaceable> <replaceable>docbook/fo/docbook.xsl</replaceable> > <replaceable>output.fo</replaceable>
java org.apache.fop.apps.CommandLine <replaceable>output.fo</replaceable> <replaceable>output.pdf</replaceable></screen>
<para>As of this writing, some other XSLT processors to
choose from include:</para>
<itemizedlist>
<listitem>
<para>4XSLT, written in Python, from FourThought LLC (
<ulink url="http://www.fourthought.com">http://www.fourthought.com</ulink>)</para>
</listitem>
<listitem>
<para>Sablotron, written in C++, from Ginger Alliance (
<ulink url="http://www.gingerall.com">http://www.gingerall.com</ulink>)</para>
</listitem>
<listitem>
<para>Saxon, written in Java, from Michael Kay (
<ulink url="http://users.iclway.co.uk/mhkay/saxon">http://users.iclway.co.uk/mhkay/saxon</ulink>)</para>
</listitem>
<listitem>
<para>Xalan, written in Java, from the Apache XML
Project (
<ulink url="http://xml.apache.org">http://xml.apache.org</ulink>)</para>
</listitem>
<listitem>
<para>XML::XSLT,written in Perl, from Geert Josten and
Egon Willighagen (
<ulink url="http://www.cpan.org">http://www.cpan.org</ulink>)</para>
</listitem>
</itemizedlist>
<para>For print output, these additional tools are available for processing formatting objects:</para>
<itemizedlist><listitem><para>XEP (written in Java) from
RenderX (
<ulink url="http://www.renderx.com">http://www.renderx.com</ulink>).</para></listitem><listitem><para>PassiveTeX from Sebastian Rahtz (<ulink url="http://users.ox.ac.uk/~rahtz/passivetex/">http://users.ox.ac.uk/~rahtz/passivetex/</ulink>).</para></listitem></itemizedlist></sect1>
<sect1>
<title>A brief introduction to XSL</title>
<para>XSL is both a transformation language and a
formatting language. The XSLT transformation part lets you
scan through a document's structure and rearrange its
content any way you like. You can write out the content
using a different set of XML tags, and generate text as
needed. For example, you can scan through a document to
locate all headings and then insert a generated table of
contents at the beginning of the document, at the same time
writing out the content marked up as HTML. XSL is also a
rich formatting language, letting you apply typesetting
controls to all components of your output. With a good
formatting backend, it is capable of producing high quality
printed pages.</para>
<para>An XSL stylesheet is written using XML syntax, and is
itself a well-formed XML document. That makes the basic
syntax familiar, and enables an XML processor to check for
basic syntax errors. The stylesheet instructions use
special element names, which typically begin with
<literal>xsl:</literal> to distinguish them from any XML
tags you want to appear in the output. The XSL namespace is
identified at the top of the stylesheet file. As with other
XML, any XSL elements that are not empty will require a
closing tag. And some XSL elements have specific attributes
that control their behavior. It helps to keep a good XSL
reference book handy.</para>
<para>Here is an example of a simple XSL stylesheet applied
to a simple XML file to generate HTML output.</para>
<example>
<title>Simple XML file</title>
<programlisting><?xml version="1.0"?>
<document>
<title>Using a mouse</title>
<para>It's easy to use a mouse. Just roll it
around and click the buttons.</para>
</document></programlisting>
</example>
<example>
<title>Simple XSL stylesheet</title>
<programlisting><?xml version='1.0'?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
<xsl:output method="html"/>
<xsl:template match="document">
<HTML><HEAD><TITLE>
<xsl:value-of select="./title"/>
</TITLE>
</HEAD>
<BODY>
<xsl:apply-templates/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="title">
<H1><xsl:apply-templates/></H1>
</xsl:template>
<xsl:template match="para">
<P><xsl:apply-templates/></P>
</xsl:template>
</xsl:stylesheet>
</programlisting>
</example>
<example>
<title>HTML output</title>
<programlisting><HTML>
<HEAD>
<TITLE>Using a mouse</TITLE>
</HEAD>
<BODY>
<H1>Using a mouse</H1>
<P>It's easy to use a mouse. Just roll it
around and click the buttons.</P>
</BODY>
</HTML>
</programlisting>
</example>
</sect1>
<sect1>
<title>XSL processing model</title>
<para>XSL is a template language, not a procedural
language. That means a stylesheet specifies a sample of the
output, not a sequence of programming steps to generate it.
A stylesheet consists of a mixture of output samples with
instructions of what to put in each sample. Each bit of
output sample and instructions is called
a <emphasis>template</emphasis>.</para>
<para>In general, you write a template for each element
type in your document. That lets you concentrate on
handling just one element at a time, and keeps a stylesheet
modular. The power of XSL comes from processing the
templates recursively. That is, each template handles the
processing of its own element, and then calls other
templates to process its children, and so on. Since an XML
document is always a single root element at the top level
that contains all of the nested descendent elements, the
XSL templates also start at the top and work their way down
through the hierarchy of elements.</para>
<para>Take the
DocBook <literal><para></literal> paragraph element as
an example. To convert this to HTML, you want to wrap the
paragraph content with the HTML
tags <literal><p></literal> and <literal></p></literal>.
But a DocBook <literal><para></literal> can contain
any number of in-line DocBook elements marking up the text.
Fortunately, you can let other templates take care of those
elements, so your XSL template
for <literal><para></literal> can be quite
simple:</para>
<programlisting><xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
</programlisting>
<para>The <literal><xsl:template></literal> element
starts a new template, and
its <literal>match</literal> attribute indicates where to
apply the template, in this case to
any <literal><para></literal> elements. The template
says to output a literal <literal><p></literal> string
and then execute
the <literal><xsl:apply-templates/></literal> instruction.
This tells the XSL processor to look among all the
templates in the stylesheet for any that should be applied
to the content of the paragraph. If each template in the
stylesheet includes
an <literal><xsl:apply-templates/></literal> instruction,
then all descendents will eventually be processed. When it
is through recursively applying templates to the paragraph
content, it outputs the <literal></p></literal> closing
tag.</para>
<sect2>
<title>Context is important</title>
<para>Since you aren't writing a linear procedure to
process your document, the context of where and how to
apply each modular template is important.
The <literal>match</literal> attribute
of <literal><xsl:template></literal> provides that
context for most templates. There is an entire expression
language, XPath, for identifying what parts of your
document should be handled by each template. The simplest
context is just an element name, as in the example above.
But you can also specify elements as children of other
elements, elements with certain attribute values, the first
or last elements in a sequence, and so on. Here is how the
DocBook <literal><formalpara></literal> element is
handled:</para>
<programlisting><xsl:template match="formalpara">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="formalpara/title">
<b><xsl:apply-templates/></b>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="formalpara/para">
<xsl:apply-templates/>
</xsl:template>
</programlisting>
<para>There are three templates defined, one for
the <literal><formalpara></literal> element itself,
and one for each of its children elements. The <literal>match</literal> attribute
value <literal>formalpara/title</literal> in the second
template is an XPath expression indicating
a <literal><title></literal> element that is an
immediate child of
a <literal><formalpara></literal> element. This
distinguishes such titles from
other <literal><title></literal> elements used in
DocBook. XPath expressions are the key to controlling how
your templates are applied.</para>
<para>In general, the XSL processor has internal rules that
apply templates that are more specific before templates
that are less specific. That lets you control the details,
but also provides a fallback mechanism to a less specific
template when you don't supply the full context for every
combination of elements. This feature is illustrated by the
third template, for <literal>formalpara/para</literal>. By
including this template, the stylesheet processes a <literal><para></literal> within <literal><formalpara></literal> in
a special way, in this case by not outputting the HTML <literal><p></literal> tags already output by its parent. If this template had not been included, then the processor would have fallen back to the template
specified by <literal>match="para"</literal> described
above, which would have output a second set of <literal><p></literal> tags.</para>
<para>You can also control template context with
XSL <emphasis>modes</emphasis>, which are used extensively
in the DocBook stylesheets. Modes let you process the same
input more than once in different ways.
A <literal>mode</literal> attribute in
an <literal><xsl:template></literal> definition adds a
specific mode name to that template. When the same mode
name is used
in <literal><xsl:apply-templates/></literal>, it acts
as a filter to narrow the selection of templates to only
those selected by
the <literal>match</literal> expression <emphasis>and</emphasis> that
have that mode name. This lets you define two different
templates for the same element match that are applied under
different contexts. For example, there are two templates
defined for
DocBook <literal><listitem></literal> elements:</para>
<programlisting><xsl:template match="listitem">
<li><xsl:apply-templates/></li>
</xsl:template>
<xsl:template match="listitem" mode="xref">
<xsl:number format="1"/>
</xsl:template>
</programlisting>
<para>The first template is for the normal list item
context where you want to output the
HTML <literal><li></literal> tags. The second template
is called with <literal><xsl:apply-templates
select="$target" mode="xref"/></literal> in the context
of processing <literal><xref></literal> elements. In
this case the <literal>select</literal> attribute locates
the ID of the specific list item and
the <literal>mode</literal> attribute selects the second
template, whose effect is to output its item number when it
is in an ordered list. Because there are many such special
needs when
processing <literal><xref></literal> elements, it is
convenient to define a mode name <literal>xref</literal> to
handle them all. Keep in mind that mode settings
do <emphasis>not</emphasis> automatically get passed down to
other templates
through <literal><xsl:apply-templates/></literal>.</para>
</sect2>
<sect2>
<title>Programming features</title>
<para>Although XSL is template-driven, it also has some
features of traditional programming languages. Here are
some examples from the DocBook stylesheets. </para>
<programlisting><lineannotation>Assign a value to a variable:</lineannotation>
<xsl:variable name="refelem" select="name($target)"/>
<lineannotation>If statement:</lineannotation>
<xsl:if test="$show.comments">
<i><xsl:call-template name="inline.charseq"/></i>
</xsl:if>
<lineannotation>Case statement:</lineannotation>
<xsl:choose>
<xsl:when test="@columns">
<xsl:value-of select="@columns"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
<lineannotation>Call a template by name like a subroutine, passing parameter values and accepting a return value:</lineannotation>
<xsl:call-template name="xref.xreflabel">
<xsl:with-param name="target" select="$target"/>
</xsl:call-template>
</programlisting>
<para>However, you can't always use these constructs as you
do in other programming languages. Variables in particular
have very different behavior.</para>
<sect3>
<title>Using variables and parameters</title>
<para>XSL provides two elements that let you assign a value
to a
name: <literal><xsl:variable></literal> and <literal><xsl:param></literal>.
These share the same name space and syntax for assigning
names and values. Both can be referred to using
the <literal>$name</literal> syntax. The main difference
between these two elements is that a param's value acts as
a default value that can be overridden when a template is
called using
a <literal><xsl:with-param></literal> element as in the
last example above.</para>
<para>Here are two examples from DocBook:</para>
<programlisting><xsl:param name="cols">1</xsl:param>
<xsl:variable name="segnum" select="position()"/>
</programlisting>
<para>In both elements, the name of the parameter or
variable is specified with
the <literal>name</literal> attribute. So the name of
the <literal>param</literal> here
is <literal>cols</literal> and the name of
the <literal>variable</literal> is <literal>segnum</literal>.
The value of either can be supplied in two ways. The value
of the first example is the text node "1" and is supplied
as the content of the element. The value of the second
example is supplied as the result of the expression in
its <literal>select</literal> attribute, and the element
itself has no content.</para>
<para>The feature of XSL variables that is odd to new users
is that once you assign a value to a variable, you cannot
assign a new value within the same scope. Doing so will
generate an error. So variables are not used as dynamic
storage bins they way they are in other languages. They
hold a fixed value within their scope of application, and
then disappear when the scope is exited. This feature is a
result of the design of XSL, which is template-driven and
not procedural. This means there is no definite order of
processing, so you can't rely on the values of changing
variables. To use variables in XSL, you need to understand
how their scope is defined.</para>
<para>Variables defined outside of all templates are
considered global variables, and they are readable within
all templates. The value of a global variable is fixed, and
its global value can't be altered from within any template.
However, a template can create a local variable of the same
name and give it a different value. That local value
remains in effect only within the scope of the local
variable.</para>
<para>Variables defined within a template remain in effect
only within their permitted scope, which is defined as all
following siblings and their descendants. To understand
such a scope, you have to remember that XSL instructions
are true XML elements that are embedded in an XML family
hierarchy of XSL elements, often referred to as parents,
children, siblings, ancestors and descendants. Taking the
family analogy a step further, think of a variable
assignment as a piece of advice that you are allowed to
give to certain family members. You can give your advice
only to your younger siblings (those that follow you) and
their descendents. Your older siblings won't listen,
neither will your parents or any of your ancestors. To
stretch the analogy a bit, it is an error to try to give
different advice under the same name to the same group of
listeners (in other words, to redefine the variable). Keep
in mind that this family is not the elements of your
document, but just the XSL instructions in your stylesheet.
To help you keep track of such scopes in hand-written
stylesheets, it helps to indent nested XSL elements. Here
is an edited snippet from the DocBook stylesheet
file <filename>pi.xsl</filename> that illustrates different
scopes for two variables:</para>
<programlisting>
1 <xsl:template name="dbhtml-attribute">
2 ...
3 <xsl:choose>
4 <xsl:when test="$count>count($pis)">
5 <!-- not found -->
6 </xsl:when>
7 <xsl:otherwise>
8 <xsl:variable name="pi">
9 <xsl:value-of select="$pis[$count]"/>
10 </xsl:variable>
11 <xsl:choose>
12 <xsl:when test="contains($pi,concat($attribute, '='))">
13 <xsl:variable name="rest" select="substring-after($pi,concat($attribute,'='))"/>
14 <xsl:variable name="quote" select="substring($rest,1,1)"/>
15 <xsl:value-of select="substring-before(substring($rest,2),$quote)"/>
16 </xsl:when>
17 <xsl:otherwise>
18 ...
19 </xsl:otherwise>
20 </xsl:choose>
21 </xsl:otherwise>
22 </xsl:choose>
23 </xsl:template>
</programlisting>
<para>The scope of the variable <literal>pi</literal> begins
on line 8 where it is defined in this template, and ends on
line 20 when its last sibling ends.<footnote><para>Technically, the scope extends to the end tag of the parent of the <literal><xsl:variable></literal> element. That is effectively the last sibling.</para></footnote> The scope of the
variable <literal>rest</literal> begins on line 13 and ends
on line 15. Fortunately, line 15 outputs an expression
using the value before it goes out of scope.</para>
<para>What happens when
an <literal><xsl:apply-templates/></literal> element
is used within the scope of a local variable? Do the
templates that are applied to the document children get the
variable? The answer is no. The templates that are applied
are not actually within the scope of the variable. They
exist elsewhere in the stylesheet and are not following
siblings or their descendants. </para>
<para>To pass a value to another template, you pass a
parameter using
the <literal><xsl:with-param></literal> element. This
parameter passing is usually done with calls to a specific
named template
using <literal><xsl:call-template></literal>, although
it works
with <literal><xsl:apply-templates></literal> too.
That's because the called template must be expecting the
parameter by defining it using
a <literal><xsl:param></literal> element with the same
parameter name. Any passed parameters whose names are not
defined in the called template are ignored.</para>
<para>Here is an example of parameter passing
from <filename>docbook.xsl</filename>:</para>
<programlisting><xsl:call-template name="head.content">
<xsl:with-param name="node" select="$doc"/>
</xsl:call-template>
</programlisting>
<para>Here a template
named <literal>head.content</literal> is being called and
passed a parameter named <literal>node</literal> whose
content is the value of the <literal>$doc</literal> variable
in the current context. The top of that template looks like
this:</para>
<programlisting><xsl:template name="head.content">
<xsl:param name="node" select="."/>
</programlisting>
<para>The template is expecting the parameter because it
has a <literal><xsl:param></literal> defined with the
same name. The value in this definition is the default
value. This would be the parameter value used in the
template if the template was called without passing that
parameter.</para>
</sect3>
</sect2>
<sect2>
<title>Generating HTML output.</title>
<para>You generate HTML from your DocBook XML files by
applying the HTML version of the stylesheets. This is done
by using the HTML driver
file <filename>docbook/html/docbook.xsl</filename> as your
stylesheet. That is the master stylesheet file that
uses <literal><xsl:include></literal> to pull in the
component files it needs to assemble a complete stylesheet
for producing HTML. </para>
<para>The way the DocBook stylesheet generates HTML is to
apply templates that output a mix of text content and HTML
elements. Starting at the top level in the main
file <filename>docbook.xsl</filename>:</para>
<programlisting><xsl:template match="/">
<xsl:variable name="doc" select="*[1]"/>
<html>
<head>
<xsl:call-template name="head.content">
<xsl:with-param name="node" select="$doc"/>
</xsl:call-template>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</programlisting>
<para>This template matches the root element of your input
document, and starts the process of recursively applying
templates. It first defines a variable
named <literal>doc</literal> and then outputs two literal
HTML elements <literal><html></literal> and <literal><head></literal>.
Then it calls a named
template <literal>head.content</literal> to process the
content of the HTML <literal><head></literal>, closes
the <literal><head></literal> and starts
the <literal><body></literal>. There it
uses <literal><xsl:apply-templates/></literal> to
recursively process the entire input document. Then it just
closes out the HTML file.</para>
<para>Simple HTML elements can generated as literal
elements as shown here. But if the HTML being output
depends on the context, you need something more powerful to
select the element name and possibly add attributes and
their values. Here is a fragment
from <filename>sections.xsl</filename> that shows how a
heading tag is generated using
the <literal><xsl:element></literal> and <literal><xsl:attribute></literal> elements:</para>
<programlisting>
1 <xsl:element name="h{$level}">
2 <xsl:attribute name="class">title</xsl:attribute>
3 <xsl:if test="$level<3">
4 <xsl:attribute name="style">clear: all</xsl:attribute>
5 </xsl:if>
6 <a>
7 <xsl:attribute name="name">
8 <xsl:call-template name="object.id"/>
9 </xsl:attribute>
10 <b><xsl:copy-of select="$title"/></b>
11 </a>
12 </xsl:element>
</programlisting>
<para>This whole example is generating a single HTML
heading element. Line 1 begins the HTML element definition
by identifying the name of the element. In this case, the
name is an expression that includes the
variable <literal>$level</literal> passed as a parameter to
this template. Thus a single template can
generate <literal><h1></literal>, <literal><h2></literal>,
etc. depending on the context in which it is called. Line 2
defines a <literal>class="title"</literal> attribute that is
added to this element. Lines 3 to 5 add
a <literal>style="clear all"</literal> attribute, but only
if the heading level is less than 3. Line 6 opens
an <literal><a></literal> anchor element. Although this
looks like a literal output string, it is actually modified
by lines 7 to 9 that insert
the <literal>name</literal> attribute into
the <literal><a></literal> element. This illustrates
that XSL is managing output elements as active element
nodes, not just text strings. Line 10 outputs the text of
the heading title, also passed as a parameter to the
template, enclosed in HTML boldface tags. Line 11 closes
the anchor tag with the
literal <literal></a></literal> syntax, while line 12
closes the heading tag by closing the element definition.
Since the actual element name is a variable, it couldn't
use the literal syntax.</para>
<para>As you follow the sequence of nested templates
processing elements, you might be wondering how the
ordinary text of your input document gets to the output. In
the file <filename>docbook.xsl</filename> you will find
this template that handles any text not processed by any
other template:</para>
<programlisting><xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</programlisting>
<para>This template's body consists of the "value" of the text node,
which is just its text. In general, all XSL processors have
some built-in templates to handle any content for which
your stylesheet doesn't supply a matching template. This
template serves the same function but appears explicitly in
the stylesheet.</para>
</sect2>
<sect2>
<title>Generating formatting objects.</title>
<para>You generate formatting objects from your DocBook XML
files by applying the fo version of the stylesheets. This
is done by using the fo driver
file <filename>docbook/fo/docbook.xsl</filename> as your
stylesheet. That is the master stylesheet file that
uses <literal><xsl:include></literal> to pull in the
component files it needs to assemble a complete stylesheet
for producing formatting objects. Generating a formatting
objects file is only half the process of producing typeset
output. You also need a formatting object processor such as
the Apache XML Project's FOP as described in an earlier
section.</para>
<para>The DocBook fo stylesheet works in a similar manner
to the HTML stylesheet. Instead of outputting HTML tags, it
outputs text marked up
with <literal><fo:<replaceable>something</replaceable>></literal> tags.
For example, to indicate that some text should be kept
in-line and typeset with a monospace font, it might look
like this:</para>
<programlisting><fo:inline-sequence font-family="monospace">/usr/man</fo:inline-sequence></programlisting>
<para>The templates
in <filename>docbook/fo/inline.xsl</filename> that produce
this output for a
DocBook <literal><filename></literal> element look
like this:</para>
<programlisting><xsl:template match="filename">
<xsl:call-template name="inline.monoseq"/>
</xsl:template>
<xsl:template name="inline.monoseq">
<xsl:param name="content">
<xsl:apply-templates/>
</xsl:param>
<fo:inline-sequence font-family="monospace">
<xsl:copy-of select="$content"/>
</fo:inline-sequence>
</xsl:template>
</programlisting>
<para>There are dozens of fo tags and attributes specified
in the XSL standard. It is beyond the scope of this
document to cover how all of them are used in the DocBook
stylesheets. Fortunately, this is only an intermediate
format that you probably won't have to deal with very much
directly unless you are writing your own
stylesheets.</para>
</sect2>
</sect1>
<sect1>
<title>Customizing DocBook XSL stylesheets</title>
<para>The DocBook XSL stylesheets are written in a modular
fashion. Each of the HTML and FO stylesheets starts with a
driver file that assembles a collection of component files
into a complete stylesheet. This modular design puts similar things together into smaller files that are easier to write and maintain than one big stylesheet. The modular stylesheet files
are distributed among four directories:</para>
<variablelist>
<varlistentry><term>common/</term>
<listitem>
<para>contains code common to both stylesheets, including localization data
</para>
</listitem>
</varlistentry>
<varlistentry><term>fo/</term>
<listitem>
<para>a stylesheet that produces XSL FO result trees
</para>
</listitem>
</varlistentry>
<varlistentry><term>html/</term>
<listitem>
<para>a stylesheet that produces HTML/XHTML result trees
</para>
</listitem>
</varlistentry>
<varlistentry><term>lib/</term>
<listitem>
<para>contains schema-independent functions
</para>
</listitem>
</varlistentry>
</variablelist>
<para>The driver files for each of HTML and FO stylesheets
are <filename>html/docbook.xsl</filename> and <filename>fo/docbook.xsl</filename>,
respectively. A driver file consists mostly of a bunch
of <literal><xsl:include></literal> instructions to
pull in the component templates, and then defines some
top-level templates. For example:</para>
<programlisting><xsl:include href="../VERSION"/>
<xsl:include href="../lib/lib.xsl"/>
<xsl:include href="../common/l10n.xsl"/>
<xsl:include href="../common/common.xsl"/>
<xsl:include href="autotoc.xsl"/>
<xsl:include href="lists.xsl"/>
<xsl:include href="callout.xsl"/>
...
<xsl:include href="param.xsl"/>
<xsl:include href="pi.xsl"/>
</programlisting>
<para>The first four modules are shared with the FO
stylesheet and are referenced using relative pathnames to
the common directories. Then the long list of component
stylesheets starts. Pathnames in include statements are
always taken to be relative to the including file. Each
included file must be a valid XSL stylesheet, which means
its root element must
be <literal><xsl:stylesheet></literal>.</para>
<sect2>
<title>Stylesheet inclusion vs. importing</title>
<para>XSL actually provides two inclusion
mechanisms: <literal><xsl:include></literal> and <literal><xsl:import></literal>.
Of the two, <literal><xsl:include></literal> is
the simpler. It treats the included content as if it were
actually typed into the file at that point, and doesn't
give it any more or less precedence relative to the
surrounding text. It is best used when assembling
dissimilar templates that don't overlap what they match.
The DocBook driver files use this instruction to assemble a
set of modules into a stylesheet.</para>
<para>In contrast, <literal><xsl:import></literal> lets
you manage the precedence of templates and variables. It is
the preferred mode of customizing another stylesheet because
it lets you override definitions in the distributed
stylesheet with your own, without altering the distribution
files at all. You simply import the whole stylesheet and
add whatever changes you want.</para>
<para>The precedence rules for import are detailed and
rigorously defined in the XSL standard. The basic rule is
that any templates and variables in the importing
stylesheet have precedence over equivalent templates and
variables in the imported stylesheet. Think of the imported stylesheet elements as a fallback collection, to be used only if a match is not found in the current stylesheet. You can customize the templates you want to change in your stylesheet file, and let the imported stylesheet handle the rest.</para>
<note>
<para>Customizing a DocBook XSL stylesheet is the opposite
of customizing a DocBook DTD. When you customize a DocBook
DTD, the rules of XML and SGML dictate that
the <emphasis>first</emphasis> of any duplicate declarations
wins. Any subsequent declarations of the same element or
entity are ignored. The architecture of the DTD provides
slots for inserting your own custom declarations early
enough in the DTD for them to override the standard
declarations. In contrast, customizing an XSL stylesheet is
simpler because your definitions have precedence over imported ones.</para>
</note>
<para>You can carry modularization to deeper levels because
module files can also include or import other modules.
You'll need to be careful to maintain the precedence that
you want as the modules get rolled up into a complete
stylesheet. </para>
</sect2>
<sect2>
<title>Customizing
with <literal><xsl:import></literal></title>
<para>There is currently one example of customizing
with <literal><xsl:import></literal> in the HTML
version of the DocBook stylesheets.
The <filename>xtchunk.xsl</filename> stylesheet modifies the
HTML processing to output many smaller HTML files rather
than a single large file per input document. It uses XSL
extensions defined only in the XSL
processor <command>XT</command>. In the driver
file <filename>xtchunk.xsl</filename>, the first instruction
is <literal><xsl:import
href="docbook.xsl"/></literal>. That instruction imports
the original driver file, which in turn uses
many <literal><xsl:include></literal> instructions to
include all the modules. That single import instruction
gives the new stylesheet the complete set of DocBook
templates to start with.</para>
<para>After the
import, <filename>xtchunk.xsl</filename> redefines some of
the templates and adds some new ones. Here is one example
of a redefined template:</para>
<programlisting><lineannotation>Original template in autotoc.xsl</lineannotation>
<xsl:template name="href.target">
<xsl:param name="object" select="."/>
<xsl:text>#</xsl:text>
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="$object"/>
</xsl:call-template>
</xsl:template>
<lineannotation>New template in xtchunk.xsl</lineannotation>
<xsl:template name="href.target">
<xsl:param name="object" select="."/>
<xsl:variable name="ischunk">
<xsl:call-template name="chunk">
<xsl:with-param name="node" select="$object"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates mode="chunk-filename" select="$object"/>
<xsl:if test="$ischunk='0'">
<xsl:text>#</xsl:text>
<xsl:call-template name="object.id">
<xsl:with-param name="object" select="$object"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</programlisting>
<para>The new template handles the more complex processing
of HREFs when the output is split into many HTML files.
Where the old template could simply
output <literal>#<replaceable>object.id</replaceable></literal>,
the new one outputs <literal><replaceable>filename</replaceable>#<replaceable>object.id</replaceable></literal>.</para>
</sect2>
<sect2>
<title>Setting stylesheet variables</title>
<para>You may not have to define any new templates,
however. The DocBook stylesheets are parameterized using
XSL variables rather than hard-coded values for many of the
formatting features. Since
the <literal><xsl:import></literal> mechanism also
lets you redefine global variables, this gives you an easy
way to customize many features of the DocBook
stylesheets. Over time, more features will be parameterized to permit customization. If you find hardcoded values in the stylesheets that would be useful to customize, please let the maintainer know.</para>
<para>Near the end of the list of includes in the main
DocBook driver file is the
instruction <literal><xsl:include
href="param.xsl"/></literal>.
The <filename>param.xsl</filename> file is the most
important module for customizing a DocBook XSL stylesheet.
This module contains no templates, only definitions of
stylesheet variables. Since these variables are defined
outside of any template, they are global variables and
apply to the entire stylesheet. By redefining these
variables in an importing stylesheet, you can change the
behavior of the stylesheet.</para>
<para>To create a customized DocBook stylesheet, you simply
create a new stylesheet file such
as <filename>mystyle.xsl</filename> that imports the standard
stylesheet and adds your own new variable definitions. Here
is an example of a complete custom stylesheet that changes
the depth of sections listed in the table of contents from
two to three:</para>
<programlisting><?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version='1.0'
xmlns="http://www.w3.org/TR/xhtml1/transitional"
exclude-result-prefixes="#default">
<xsl:import href="docbook.xsl"/>
<xsl:variable name="toc.section.depth">3</xsl:variable>
<!-- Add other variable definitions here -->
</xsl:stylesheet>
</programlisting>
<para>Following the opening stylesheet element are the
import instruction and one variable definition. The
variable <literal>toc.section.depth</literal> was defined
in <filename>param.xsl</filename> with value "2", and here
it is defined as "3". Since the importing stylesheet takes
precedence, this new value is used. Thus documents
processed with <filename>mystyle.xsl</filename> instead
of <filename>docbook.xsl</filename> will have three levels
of sections in the tables of contents, and all other
processing will be the same.</para>
<para>Use the list of variables
in <filename>param.xsl</filename> as your guide for creating
a custom stylesheet. If the changes you want are controlled
by a variable there, then customizing is easy. </para>
</sect2>
<sect2>
<title>Writing your own templates</title>
<para>If the changes you want are more extensive than what
is supported by variables, you can write new templates. You
can put your new templates directly in your importing
stylesheet, or you can modularize your importing stylesheet
as well. You can write your own stylesheet module
containing a collection of templates for processing lists,
for example, and put them in a file
named <filename>mylists.xsl</filename>. Then your importing
stylesheet can pull in your list templates with
a <literal><xsl:include
href="mylists.xsl"/></literal> instruction. Since your
included template definitions appear after the main import
instruction, your templates will take precedence.</para>
<para>You'll need to make sure your new templates are
compatible with the remaining modules, which means:</para>
<itemizedlist>
<listitem>
<para>Any named templates should use the same name so
calling templates in other modules can find them.</para>
</listitem>
<listitem>
<para>Your template set should process the same elements
matched by templates in the original module, to ensure
complete coverage.</para>
</listitem>
<listitem>
<para>Include the same set
of <literal><xsl:param></literal> elements in each
template to interface properly with any calling templates,
although you can set different values for your
parameters.</para>
</listitem>
<listitem>
<para>Any templates that are used like subroutines to
return a value should return the same data type.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Writing your own driver</title>
<para>Another approach to customizing the stylesheets is to
write your own driver file. Instead of
using <literal><xsl:import
href="docbook.xsl"/></literal>, you copy that file to a
new name and rewrite any of
the <literal><xsl:include/></literal> instructions to
assemble a custom collection of stylesheet modules. One
reason to do this is to speed up processing by reducing the
size of the stylesheet. If you are using a customized
DocBook DTD that omits many elements you never use, you
might be able to omit those modules of the
stylesheet.</para>
</sect2>
<sect2>
<title>Localization</title>
<para>The DocBook stylesheets include features for
localizing generated text, that is, printing any generated
text in a language other than the default English. In
general, the stylesheets will switch to the language
identified by a <literal>lang</literal> attribute when
processing elements in your documents. If your documents
use the <literal>lang</literal> attribute, then you don't
need to customize the stylesheets at all for
localization.</para>
<para>As far as the stylesheets go,
a <literal>lang</literal> attribute is inherited by the
descendents of a document element. The stylesheet searches
for a <literal>lang</literal> attribute using this XPath
expression:</para>
<programlisting><xsl:variable name="lang-attr"
select="($target/ancestor-or-self::*/@lang
|$target/ancestor-or-self::*/@xml:lang)[last()]"/></programlisting>
<para>This locates the attribute on the current element or
its most recent ancestor. Thus
a <literal>lang</literal> attribute is in effect for an
element and all of its descendents, unless it is reset in
one of those descendents. If you define it in only your
document root element, then it applies to the whole
document:</para>
<programlisting><?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.0//EN" "docbook.dtd">
<book lang="fr">
...
</book></programlisting>
<para>When text is being generated, the stylesheet checks
the most recent <literal>lang</literal> attribute and looks
up the generated text strings for that language in a
localization XML file. These are located in
the <filename>common</filename> directory of the
stylesheets, one file per language. Here is the top of the
file <filename>fr.xml</filename>:</para>
<programlisting><localization language="fr">
<gentext key="abstract" text="R&#x00E9;sum&#x00E9;"/>
<gentext key="answer" text="R:"/>
<gentext key="appendix" text="Annexe"/>
<gentext key="article" text="Article"/>
<gentext key="bibliography" text="Bibliographie"/>
...
</programlisting>
<para>The stylesheet templates use the gentext key names,
and then the stylesheet looks up the associated text value
when the document is processed with that lang setting. The
file <filename>l10n.xml</filename> (note
the <filename>.xml</filename> suffix) lists the filenames of
all the supported languages.</para>
<para>You can also create a custom stylesheet that sets the
language. That might be useful if your documents don't make
appropriate use of the <literal>lang</literal> attribute.
The module <filename>l10n.xsl</filename> defines two global
variables that can be overridden with an importing
stylesheet as described above. Here are their default
definitions:</para>
<programlisting><xsl:variable name="l10n.gentext.language"></xsl:variable>
<xsl:variable name="l10n.gentext.default.language">en</xsl:variable>
</programlisting>
<para>The first one sets the language for all elements,
regardless of an element's <literal>lang</literal> attribute
value. The second just sets a default language for any
elements that haven't got a <literal>lang</literal> setting
of their own (or their ancestors).</para>
</sect2>
</sect1>
</chapter>
|