Introduction
This article will cover a number of topics, but it's main point is to show you how to get up an running with producing documentation using the DocBook help processor, and then creating customizations for producing plain html file for use on the web or offline, or in making a single Microsoft HTML Help file (.chm) for windows. By the end of the article I'll show you how to create and process your own DocBook file(s), customize the style sheets for it's output, and create a simple makefile to run the whole thing from a command line. The article will mention a number of technologies, and my aim is not to explain in detail how they work, but mainly explain how to use them to create html based documentation for Win32 (though much of this is generic to pretty much any OS that can run the various DocBook tools) systems. This is based on my experiences with DocBook in using it to produce the documentation for my VCF project that I spend much of my time on.
DocBook is a very cool XML-based syntax that allows you to author documentation in a single format, and then run it through various processors to create your final documentation output. From a single DocBook source you can output html, PDF, latex, rtf, and many other formats. We'll focus on dealing with html and HTML Help. Once you have written your DocBook files you have a choice in how they are processed. The original way was through a DSSSL processor such as Jade or OpenJade. DSSSL has a lisp like syntax and is a bit unwieldy to use. The other way, and the one apparently most people use, is XSL style sheets run through an xsl processor, like xsltproc. You can use other processors for XSL but many of them will not work correctly, and xsltproc is nice because it doesn't require a Java runtime to be installed. I found this out the hard way after spending the better part of a day downloading and trying several different XSL processors!
One of the many neat things about DocBook, is that from the single, plain text source, the customization style sheets can automatically produce index, table of contents, and all sorts of extra stuff, all without you having to put much effort into it in terms of extra writing in your documentation.
Before we do anything though, we are going to have to get some tools set up. When working with DocBook on Windows, half the hassle is getting the tools and your environment set up right. So, first get the following tools:
- Cygwin - this is free tool that provides a whole slew of command line tools that are typical with *nix systems. Cygwin is, simply put, and awesome collection of tools for doing command line work - I can't imagine working without it. Some of the tools included (in no particular order) are ssh, cvs, gcc, make, touch, scp, more, less, cat, bash, perl, python, and many, many others. The setup is trivial to do (you will need be online though), and allows you to pick and choose exactly what you want. For our purposes make sure you have at least the following selected:
- bash (this the shell you use in *nix, and will let you run all sort of cool shell scripts)
- make (or gmake)
- libxslt (this should also install the xsltproc program)
- sed
- tar
- gzip
- find
One note: when you come to the "Select Install Directory" page make sure to click on the "DOS" choice for Default Text Type (trust me, this will save you a lot of headaches later on).
Most of these will come with the default Cygwin installer's selection of tools. The one's to watch for are libxslt and sed. If you miss something, you can always run the installer again and select the missing programs.
-
DocBook - Next you need the DocBook XSL style sheets, you get them at the SourceForge website. Look for the file labeled docbook-xsl-1.60.1.zip (in the docbook-xsl files section). Once you've downloaded these, unzip them anywhere you want.
-
Microsoft's HTML Help Workshop will allow you to edit and compile HTML Help chm files, both from the GUI tool as well as a command line program. We'll be using the command line version (hhc.exe) of the tool in this article.
Now open up your bash command line and type (you can do this via Start > Programs > Cygwin > Cygwin Bash Shell):
xsltproc -version
You should get:
$ xsltproc -version
Using libxml 20423, libxslt 10013 and libexslt 705
xsltproc was compiled against libxml 20417, libxslt 10013 and libexslt 705
libxslt 10013 was compiled against libxml 20417
libexslt 705 was compiled against libxml 20417
Or something similar. If you don't get this and instead see an error message you may have to go and get the libxslt package - see above about notes on the Cygwin installer.
Now we can write a simple test. Create a file called simple.xml and copy and paste the following into it:
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
<book>
<title>Simple Book</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
<chapter><title>On Foo's</title>
<para>
Stuff about Foo's goes here.
</para>
</chapter>
<chapter><title>On Bars's</title>
<para>
Stuff about Bars's goes here.
</para>
</chapter>
</book>
This is a simple example that create a basic unit, a "book", and adds a title, preface, and two chapters. Your basic building blocks in terms of organizing the various sections are a book, a chapter, and a section. Chapters can nest under a book, and sections can nest under a chapter or another section. Paragraphs are wrapped with the <para>
tags and can occur most anywhere you want.
To produce our html lets run this file through our XSL processor. To use the xsltproc program, you pass it the file name of an XSL style sheet to use, and a file name of the XML file to process. The file that gets processed must be a valid XML file, if not xsltproc will whine and complain and will not process it. Specifically, you must have the <?xml version="1.0"?>
right at the top of the file, otherwise you'll scratch your head and curse fluently at your machine for not processing your perfectly valid XML file. Also when passing in file names, make sure to use the "/" and not the "\" - failure to so this will cause xsltproc to trip up and not work correctly.
So lets type (assuming your DocBook XSL files are in the d:\docbook-xsl-1.60.1 directory ):
xsltproc --nonet D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl simple.xml
Running this will produce output like this
$ xsltproc --nonet D:/dork/DocBook/xsl/htmlhelp/htmlhelp.xsl simple.xml
Attempt to load network entity http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd
Writing pr01.html for preface
Writing ch01.html for chapter
Writing ch02.html for chapter
Writing index.html for book
Writing htmlhelp.hhp
Writing toc.hhc
Now you can open up index.html in a browser and view your documentation!
The --nonet option tells xsltproc to not attempt to connect via the network and attempt to verify the DTD. I use this so I don't have to fight with producing documentation when I don't have a valid or reasonable fast network connection. For other kinds of XSL processing this may be an issue, but for DocBook processing --nonet seems to work just fine.
The first thing you may want to do is break up your documentation into multiple files, as a single file may not be the easiest thing to work with. Luckily this is easy to do. If you create a two files, chap1.xml and chap2.xml, and copy and paste the following into chap1.xml
<chapter><title>On Foo's</title>
<para>
Stuff about Foo's goes here.
</para>
</chapter>
And the following into chap2.xml
<chapter><title>On Bars's</title>
<para>
Stuff about Bars's goes here.
</para>
</chapter>
And modify simple.xml's contents like so:
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
]>
<book>
<title>Simple Book</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
&chap1;
&chap2;
</book>
The use of the <!ENTITY>
tags creates entities named chap1
and chap2
. Using them automatically includes their contents in to the simple.xml file, so be careful to not put the <?xml?>
preprocessor tags in the included files (chap1.xml and chap2.xml respectively). We can verify that all this works by running xsltproc again:
xsltproc --nonet D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl simple.xml
Again we get the same output as before and we have now broken apart our documentation into multiple files for easier editing.
Now lets generate our chm. To do this we simply call the command line HTML help compiler (hhc.exe) and pass the generated hhp file.
hhc htmlhelp.hhp
And voila! we now have our chm file:
Adding an index
Now that we can create out DocBook output and create our chm file, lets get a little fancier and add support for an index. In DocBook it's so simple we can do this in a single line, so let's modify simple.xml like so:
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
]>
<book>
<title>Simple Book</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
&chap1;
&chap2;
<index id="index"/>
</book>
This now gives indexing support. However to actually ensure an item is placed in the index, we have to indicate which items we want to mark as being indexed. Doing so is simple, we just use the <indexterm>
tag and give the term a name. So let's modify both chap1.xml and chap2.xml and add some index support:
chap1.xml:
<chapter><title>On Foo's</title>
<para><FONT size=2><indexterm><primary>About Foo's</primary></indexterm></FONT>
Stuff about Foo's goes here.
</para>
</chapter>
chap2.xml:
<chapter><title>On Bars's</title>
<para><FONT size=2><indexterm><primary>About Bar's</primary></indexterm></FONT>
Stuff about Bars's goes here.
</para>
</chapter>
Now just run it through the xsltproc to produce our html output. Then run the compile step again with hhc. Now one thing to note at this point, there seems to be a bug, because once you add index support, the HTML Help compiler will complain about a mysteriously missing ix01.html file. It will however properly generate the chm file, complete with indexing support, so I don't know what the deal is. However it can cause problems with other programs that may run the hhc program from a script as it will now return an error code, despite successfully creating the chm! We'll address this a bit more later on. And now for our obligatory screenshot:
Kewl!
Now, lets add one more thing: how to produce a single html file, such as we might want if we were to use DocBook to write an article for CodeProject. To do this, we'll use the same XML file(s), remember, single documentation source, multiple outputs, but this time specify a different XSL style sheet to use. We will also not produce any chm file, but only a single html output file. To run the processor we simply type:
xsltproc --nonet D:/docbook-xsl-1.60.1/html/docbook.xsl simple.xml
Note the change in style sheets, and the fact that it simply dumps the output to stdout. No worries though - we can just dump the output to a file like so:
xsltproc --nonet D:/docbook-xsl-1.60.1/html/docbook.xsl simple.xml > simple.html
Now we can look out our DocBook documentation, complete with index!
More DocBook tags
All right, up till now we've only seen some pretty basic usage of the various DocBook tags available. While I am not going to attempt to cover all of them, I will try cover some of the more useful one, at least in my experience. For a more thorough article on this, please see the online version of "DocBook: The Definitive Guide" from O'Reilly.
Meta data
With DocBook you can describe various peices of information about the documentation, such as the author, legal notices, book version, and copyright(s) notice. We start this by adding a <bookinfo>
tag.
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
]>
<book>
<bookinfo>
</bookinfo>
<title>Simple Book</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
&chap1;
&chap2;
<index id="index"/>
</book>
Now we can add a quick legal notice, author, and copyright sections to our simple.xml file:
<book>
<bookinfo>
<legalnotice>
<para>
Here a short legal notice: You agree that all your base
belongs to me!
</para>
</legalnotice>
<author>
<firstname>Bob</firstname>
<surname>Grey</surname>
</author>
<copyright>
<year>2003</year>
<year>2021</year>
<holder>Pennywise the Clown</holder>
</copyright>
</bookinfo>
<title>Simple Book</title>
</book>
Tables
Tables are critical for just about any kind of documentation, and DocBook fully supports them. Lets add a simple one to our chap1.xml file:
<chapter><title>On Foo's</title>
<para><FONT size=2><indexterm><primary>About Foo's</primary></indexterm></FONT>
Stuff about Foo's goes here.
</para>
<para>And now for some data in a table:
<table frame="none" pgwide="1">
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>Column 1</entry>
<entry>Column 2</entry>
<entry>Column 3</entry>
</row>
</thead>
<tbody>
<row>
<entry>Heres</entry>
<entry>A</entry>
<entry>Row entry!</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</chapter>
Notice the <tgroup>
- this is critical to have, otherwise the table will not get rendered correctly. When we process this, we'll see some thing like this:
Stuff about Foo's goes here.
And now for some data in a table:
Table 1.1.
Column 1 | Column 2 | Column 3 |
---|
Heres | A | Row entry! |
|
Note that DocBook automatically add table numbering for you and ties it to your chapters numbers. If we had 3 tables in chapter 2, we'd see them numbered as Table 2.1, Table 2.2, and Table 2.3. We'll see how to turn off the table numbering later on.
Links
In DocBook there are several way to link to things. I'll show you how to link to external URL's and to items within your DocBook documentation.
To link to external URL's you use the <ulink>
tag as follows:
chap2.xml:
<chapter><title>On Bars's</title>
<para><FONT size=2><indexterm><primary>About Bar's</primary></indexterm></FONT>
Stuff about Bars's goes here.
</para>
<para>To learn more about the wonderful world of Bar's look
<ulink url="http://www.google.com/search?q=Bars">
here
</ulink>
</para>
</chapter>
The url
attribute specifies the link to load.
Graphics
Now we certainly couldn't write documentation without graphics! Adding graphics into your documentation is accomplished with the <graphic>
tag. For example:
chap2.xml:
<chapter><title>On Bars's</title>
<para><FONT size=2><indexterm><primary>About Bar's</primary></indexterm></FONT>
Stuff about Bars's goes here.
</para>
<para>To learn more about the wonderful world of Bar's look
<ulink url="http://www.google.com/search?q=Bars">
here
</ulink>
</para>
<para>
Don't forget: Graphics are important!
<graphic fileref="smiley.bmp"></graphic>
</para>
</chapter>
Note the fileref
attribute - this is what points to the file. Fileref
can point to a url, for example, fileref="</FONT>http://images.sourceforge.net/head_bg_new.gif"
is also valid syntax.
Notes
Adding notes is another really cool feature of DocBook. A note is a little paragraph that stands out, usual with some kind of extra, or special case documentation about a feature. For example:
chap2.xml:
<chapter><title>On Bars's</title>
<para><FONT size=2><indexterm><primary>About Bar's</primary></indexterm></FONT>
Stuff about Bars's goes here.
</para>
<para>To learn more about the wonderful world of Bar's look
<ulink url="http://www.google.com/search?q=Bars">
here
</ulink>
</para>
<para>
Don't forget: Graphics are important!
<graphic fileref="smiley.bmp"></graphic>
</para>
<note>
Not only can DocBook do graphics, but it can handle notes as well!
Isn't that just cool?
</note>
</chapter>
DocBook renders this as:
Stuff about Bars's goes here.
To learn more about the wonderful world of Bar's look here
Don't forget: Graphics are important!
NoteNot only can DocBook do graphics, but it can handle notes as well! Isn't that just cool?
|
Special Formatting
In general formatting is not something that is relevant to DocBook. The idea is that DocBook is for specifying content and formatting is handled via CSS style sheets. However there are a few tags you can use. For example, the most common is the <emphasis>
tag, used like so:
chap1.xml file:
<chapter><title>On Foo's</title>
<para><FONT size=2><indexterm><primary>About Foo's</primary></indexterm></FONT>
Stuff about Foo's goes here.
</para>
<para>And now for some data in a table:
<table frame="none" pgwide="1">
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>Column 1</entry>
<entry>Column 2</entry>
<entry>Column 3</entry>
</row>
</thead>
<tbody>
<row>
<entry>Heres</entry>
<entry>A</entry>
<entry>Row entry!</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
Not only are Foo's important to proper software development, but they are
critical to understanding the synergistic relationship between Neo
<emphasis>and</emphasis> Trinity.
</para>
</chapter>
DocBook renders it like so:
Stuff about Foo's goes here.
And now for some data in a table:
Table 1.1.
Column 1 | Column 2 | Column 3 |
---|
Heres | A | Row entry! |
Not only are Foo's important to proper software development, but they are critical to understanding the synergistic relationship between Neo and Trinity.
|
Plain text formatting
Sometimes you'll want to include program listings, or plain unformatted text, say a console dump to demonstrate a command line program. For source listing, a frequent way to do this is the <programlisting>
tag. For example:
chap1.xml file:
<chapter><title>On Foo's</title>
<para><FONT size=2><indexterm><primary>About Foo's</primary></indexterm></FONT>
Stuff about Foo's goes here.
</para>
<para>And now for some data in a table:
<table frame="none" pgwide="1">
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>Column 1</entry>
<entry>Column 2</entry>
<entry>Column 3</entry>
</row>
</thead>
<tbody>
<row>
<entry>Heres</entry>
<entry>A</entry>
<entry>Row entry!</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
Not only are Foo's important to proper software development, but they are
critical to understanding the synergistic relationship between Neo
<emphasis>and</emphasis> Trinity.
</para>
<para>
Here's an example of a code listing:
<programlisting>
int foo = 12 * 23;
multiply_endlessly( foo );
</programlisting>
</para>
</chapter>
A frequent thing you need in a program listing is the use of the <
or >
characters. If you simple use them in your content, you'll get errors when you go to process the DocBook files. However, if you use the CDATA
tags you can put any characters you please in the listing, they'll be ignored till the processor encounters the closing CDATA
tag. An example:
chap1.xml file:
<chapter><title>On Foo's</title>
<para><FONT size=2><indexterm><primary>About Foo's</primary></indexterm></FONT>
Stuff about Foo's goes here.
</para>
<para>And now for some data in a table:
<table frame="none" pgwide="1">
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>Column 1</entry>
<entry>Column 2</entry>
<entry>Column 3</entry>
</row>
</thead>
<tbody>
<row>
<entry>Heres</entry>
<entry>A</entry>
<entry>Row entry!</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
Not only are Foo's important to proper software development, but they are
critical to understanding the synergistic relationship between Neo
<emphasis>and</emphasis> Trinity.
</para>
<para>
Here's an example of a code listing:
<programlisting>
<![CDATA[int foo = 12 * 23;
std::vector<int> vec;
vec.push_back( foo );
</programlisting>
</para>
</chapter>
Using the CDATA
tag makes it much easier to handle special characters that are common in programming, but are also used in XML, and it's much nicer to read than a whole bunch of "<" tags.
Faqs
Writing FAQ's is common with technical documentation, particularly if it is documentation that is destined for online usage. DocBook provides some neat tags that support this as well. To do this, lets create a new xml file, faqs.xml, and add the appropriate entity to our main xml file, like so:
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
<!ENTITY faqs SYSTEM "faqs.xml">
]>
<book>
<bookinfo>
<legalnotice>
<para>
Here a short legal notice: You agree that all your base
belongs to me!
</para>
</legalnotice>
<author>
<firstname>Bob</firstname>
<surname>Grey</surname>
</author>
<copyright>
<year>2003</year>
<year>2021</year>
<holder>Pennywise the Clown</holder>
</copyright>
</bookinfo>
<title>Simple Book</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
<preface>
&chap1;
&chap2;
&faqs;
<index id="index"/>
</book>
Now edit your faqs.xml like so:
<section><title>FAQs</title>
<qandaset><title></title>
<qandaentry>
</qandaentry>
</qandaset>
</section>
Faqs can be organized in question and answer sets, which provide a way to organize the individual questions and answers into logical groups. The sets can be broken into entries (<qandaentry>
tags), and then each entry can have it's series of question and answer tags. To fill this out, lets add some arbitrary question and answers, which, hopefully, will impart our deep and profound wisdom unto the reader:
<section><title>FAQs</title>
<qandaset><title>The Big Questions</title>
<qandaentry>
<question>
<para>Why did the chicken cross the road?</para>
</question>
<answer>
<para>
To get to the chicken feed.
</para>
</answer>
<question>
<para>Will anyone, in fact, ever need more than 640K of memory?</para>
</question>
<answer>
<para>
Two words: "Doom III".
</para>
</answer>
</qandaentry>
</qandaset>
<qandaset><title>Even Bigger Questions!</title>
<qandaentry>
<question>
<para>Is Neo really the One?</para>
</question>
<answer>
<para>
That all depends on your perception of both reality, and your
understanding of sub-atomic quantum scale micro causality. In
addition, this will require a support call to Technical Support.
Please call 1-888-232-1NEO.
</para>
</answer>
<question>
<para>Is VB really cooler than C++?</para>
</question>
<answer>
<para>
No. Thanks for playing.
</para>
</answer>
</qandaentry>
</qandaset>
</section>
Save this, process it, and we see something like:
Even Bigger Questions!
- 1. Is Neo really the One?
- 1. Is VB really cooler than C++?
1. |
Is Neo really the One? | |
That all depends on your perception of both reality, and your understanding of sub-atomic quantum scale micro causality. In addition, this will require a support call to Technical Support. Please call 1-888-232-1NEO | 1. |
Is VB really cooler than C++? | |
No. Thanks for playing. |
|
Customizing the Style Sheet
So far we have covered the basics of producing our DocBook content, but at some point you're going to want to control what it's output looks like. As an example of what you can do, I humbly submit my own work with it, the VCF Documentation. This content is simply regular DocBook, but through heavy customization of the style sheets I have been able to control things like header, footers, CSS, where standard DocBook images are looked for, and even how internal links are formatted. So to get started lets create our style sheet, and call it simple.xsl. Then lets add the boiler plate basics:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
</xsl:stylesheet>
Note the <xsl:import>
tag, this tells the processor to import, or include, the URL referred to by the href
attribute. In our case we are going to refer to our local htmlhelp.xsl style sheet. This is the same style sheet that we were using earlier in the arguments that we passed to the xsltproc program, only now we are "inheriting" from it by including it here. From here on out our command line to xsltproc will look like this:
xsltproc --nonet simple.xsl simple.xml
This will now replace the default HTML Help style sheet with our custom style sheet, which we will now start to add our specific changes to.
The style sheets use XSL which is, as I understand it, and I am by no means an expert here, a sort of XML like programming syntax that lets you tell the processor (in our case xsltproc) what to do with the XML tree that is built as a result of a successful parsing of the XML source file (in our case simple.xml).
Our first customizations will be simple ones, where we'll see how to turn on (or off) various parameters that DocBook uses in it's style sheets. So the first thing we'll do is to ensure that the legal info we have is generated (and linked to) as a separate output file (in this case html). To do this we add the following:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
</xsl:stylesheet>
Note how we did this: by add a new line using the <xsl:param>
tag we can specify a certain parameter's value. In this case we specified that the "generate.legalnotice.link
" parameter should have a value of 1, or true.
Next we'll make sure that the "Next" and "Back" navigation links at the bottom of each page is enabled. When generating HTML Help output, the default style sheet turns this off, meaning that we won't see it. We are going to turn it on here:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
</xsl:stylesheet>
We select 0, or false, into the "suppress.navigation"
variable to ensure that the navigation links are made visible.
The next set will tell the DocBook to make use of the standard DocBook graphics for tags like <note>
. DocBook provides a standard set of images for various tags.
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admin.graphics" select="1"/>
</xsl:stylesheet>
Once you have turned the default DocBook images on, you also need to specify where they live. By default, the links to them will be "images/<imagename>.png"
. So if you do not have an "images" directory and you have no png images, then you'll end up with the ugly broken image warning in the browser's rendering of the page. So, to fix this, lets tell DocBook what directory our images will be in:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admin.graphics.path">gfx/</xsl:param>
</xsl:stylesheet>
Now lets create our images directory:
mkdir gfx
Then just copy over all the png files from your DB's "images" directory (in our case, D:/docbook-xsl-1.60.1/images) into your new gfx directory.
process your DocBook files through xsltproc and you should see this:
Stuff about Bars's goes here.
To learn more about the wonderful world of Bar's look here
Don't forget: Graphics are important!
| Note |
---|
Not only can DocBook do graphics, but it can handle notes as well! Isn't that just cool? |
|
Now the note we added has a little hand pointer graphic! This happens whenever you use the <note>
tag, with no worrying about the graphics content. You can create your own image to use as well, so long as it is in the gfx directory and named note.png.
Adding CSS support
OK so we have seen how to control some of the basic variables used in outputting our processed DocBook documentation. Now lets look at how to control the content look via CSS style sheets. First let's create a simple CSS file called simple.css. Then add the following to it:
a, body, div, table, td
{
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}
body
{
background-color: #DDDDDD;
color: #000000;
margin: 0px 0px 0px 0px;
padding : 0px 5px 5px 5px;
border: 1px solid #000000;
}
If you're not familiar with how CSS works, here's a Google link to a variety of potential information about it. Basically we have declared that any <a>
, <body>
, <div>
, <table>
, or <td>
tags will use some sort of sans serif font, preferably a font named Verdana, Geneva, Arial, or Helvetica. Furthermore, we have declared that the <body>
tag should use a background color of light grey, a text color of black, have no margins whatsoever, and have a 1 pixel solid black border.
To actually get DocBook to use this CSS sheet, we need to now customize out simple.xsl file, as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
</xsl:stylesheet>
This instructs DocBook to use the CSS file that we specify in the "select" attribute, in our case the simple.css file. If you run the processor again, you'll see that we now have html that uses the style sheets specifications!
More Style Sheet customizations
There are a lot of things you can do when customizing the style sheets, so we'll look at a few more simple parameters that we can change, as well as some advanced changes, like adding a header and footer.
Lets adjust the file name for the chm file that will be produced when we run the hhc HTML Help compiler. Lets once again change simple.xsl to the following:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
</xsl:stylesheet>
Adding this line now lets us control the name of the chm file that is produced!
Next we'll specify whether or not we should create a binary TOC or not, and whether the TOC icons should be folders or the more traditional book icon.
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
</xsl:stylesheet>
Next, we'll control how deep various sections should be shown. Each time you nest a <section>
tag inside another <section>
tag it causes a new level of numbering (i.e. 1.1, and it's first child 1.1.1). The top of each page can display a certain amount of the pages sections and sub sections in a TOC style set of links. By adjusting the style sheet, we can control how deep this goes.
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
</xsl:stylesheet>
Next lets move on to adding a custom html header to the top of all the pages. To do this we'll override the default behavior for the XSL processing of a certain template. So the first thing we'll add is the initial tags for overriding the template, like so:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
<xsl:template name="user.header.navigation">
</xsl:template>
</xsl:stylesheet>
Now within the <xsl:template>
tag we can put whatever HTML we want, and it will automatically be added to the beginning of each page, after the <body>
tag, and before any of our DocBook content! So lets try this:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
<xsl:template name="user.header.navigation">
<hr>
<p>Documentation by ACME Data Inc. No Coyote's allowed.</p>
<hr>
</xsl:template>
</xsl:stylesheet>
Our custom header simply consists of two horizontal rules and some silly text in between them. However if we process this we get some strange errors:
$ xsltproc --nonet simple.xsl simple.xml
simple.xsl:22: error: Opening and ending tag mismatch: hr and xsl:template
</xsl:template>
^
simple.xsl:23: error: Opening and ending tag mismatch: hr and xsl:stylesheet
</xsl:stylesheet>
^
simple.xsl:23: error: Premature end of data in tag xsl:template
</xsl:stylesheet>
^
simple.xsl:23: error: Premature end of data in tag xsl:stylesheet
</xsl:stylesheet>
^
cannot parse simple.xsl
Unfortunately the xsltproc is expecting XML compliant tags, while standard HTML does not enforce this. So while <hr>
is perfectly acceptable HTML syntax it is not acceptable to XML parsers. Luckily we can write <hr></hr>
and still be acceptable to browsers. So a quick change:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
<xsl:template name="user.header.navigation">
<hr></hr>
<p>Documentation by ACME Data Inc. No Coyote's allowed.</p>
<hr></hr>
</xsl:template>
</xsl:stylesheet>
Once processed we something like the following (minus the changes the CSS would have made):
Documentation by ACME Data Inc. No Coyote's allowed.
Copyright © 2003, 2021 Pennywise the Clown
|
Note our custom header at the top - this would appear on each page we navigate to!
Adding a custom footer is quite similar, first we add our <xsl:template>
overrides:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
<xsl:template name="user.header.navigation">
<hr></hr>
<p>Documentation by ACME Data Inc. No Coyote's allowed.</p>
<hr></hr>
</xsl:template>
<xsl:template name="user.footer.navigation">
</xsl:template>
</xsl:stylesheet>
Note we are customizing the "user.footer.navigation"
template, as opposed to the previous customization of the "user.header.navigation"
template. Finally lets add our html footer:
simple.xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="D:/docbook-xsl-1.60.1/htmlhelp/htmlhelp.xsl"/>
<xsl:param name="generate.legalnotice.link" select="1"/>
<xsl:param name="suppress.navigation" select="0"/>
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">gfx/</xsl:param>
<xsl:param name="html.stylesheet" select="'simple.css'"/>
<xsl:param name="htmlhelp.chm" select="'simple.chm'"/>
<xsl:param name="htmlhelp.hhc.binary" select="0"/>
<xsl:param name="htmlhelp.hhc.folders.instead.books" select="0"/>
<xsl:param name="toc.section.depth" select="4"/>
<xsl:template name="user.header.navigation">
<hr></hr>
<p>Documentation by ACME Data Inc. No Coyote's allowed.</p>
<hr></hr>
</xsl:template>
<xsl:template name="user.footer.navigation">
<hr></hr>
<p>The Road Runner was here - All Wrongs Reserved.</p>
<hr></hr>
</xsl:template>
</xsl:stylesheet>
And once again, if we process this, like before, we'll now see this footer applied to each page of our documentation!
Adding automatic version numbering: fun with sed
At this point we've covered most of the basics of actually using DocBook and processing our content. However, what we frequently want to do with our documentation is to have some section of it labeled with a version, one that matches up with the software it is documenting. As far as I know DocBook offers no real support for this, so we'll look at doing it the "hard" way. The process I'll describe is a pretty typical one for OSS projects. I don't know how frequently it is used in the Win32 world, but it does work, and is pretty flexible. What we'll end up with is a special file that keeps our version number. Then we'll write a simple script that reads the file into a variable, and then performs a find-and-replace in our simple.xml where we'll have referenced the version number using a variable name, which will then be replaced. To accomplish this we'll use sed, a unix program that can, among many other things, perform this find and replace operation for us.
First lets create a file appropriately called, version.txt. Add the following line to it:
1.2.3
Verify it's contents by using the cat command:
$ cat version.txt
1.2.3
Now alter the simple.xml to use a version variable to describe it's documentation version.
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
<!ENTITY faqs SYSTEM "faqs.xml">
]>
<book>
<bookinfo>
<legalnotice>
<para>
Here a short legal notice: You agree that all your base
belongs to me!
</para>
</legalnotice>
<author>
<firstname>Bob</firstname>
<surname>Grey</surname>
</author>
<copyright>
<year>2003</year>
<year>2021</year>
<holder>Pennywise the Clown</holder>
</copyright>
</bookinfo>
<title>Simple Book Version BOOK_VERSION</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
&chap1;
&chap2;
&faqs;
<index id="index"/>
</book>
Note where we added the "Version BOOK_VERSION" the text "BOOK_VERSION" will be used as a place holder for the actual version stored in the version.txt file.
Next lets assign a variable in the shell to the contents of the version.txt file:
$ bk_ver=`cat version.txt`
Notice the use of the ` `, or back quotes. This tells the shell to assign to the variable bk_ver
whatever the output is that came from executing the text in the back quotes. To test this:
$ echo $bk_ver
1.2.3
So far so good. Now lets get really icky and introduce our little sed script:
sed "s?BOOK_VERSION?$bk_ver?g" simple.xml > simple.xml.tmp
This will find the string "BOOK_VERSION" in the file simple.xml and replace it with the contents of the variable bk_ver, and then dump the output of all this to the file simple.xml.tmp. The original file, simple.xml, will be left alone for now.
Run this, and simple.xml.tmp now contains:
="1.0"
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY chap1 SYSTEM "chap1.xml">
<!ENTITY chap2 SYSTEM "chap2.xml">
<!ENTITY faqs SYSTEM "faqs.xml">
]>
<book>
<bookinfo>
<legalnotice>
<para>
Here a short legal notice: You agree that all your base
belongs to me!
</para>
</legalnotice>
<author>
<firstname>Bob</firstname>
<surname>Grey</surname>
</author>
<copyright>
<year>2003</year>
<year>2021</year>
<holder>Pennywise the Clown</holder>
</copyright>
</bookinfo>
<title>Simple Book Version 1.2.3</title>
<titleabbrev>Simple</titleabbrev>
<preface><title>Introduction</title>
<para>
Hello! Here's an introduction!
</para>
</preface>
&chap1;
&chap2;
&faqs;
<index id="index"/>
</book>
Notice our version number!
Now we can just copy a temporary version of simple.xml (say to simple.xml.orig), rename simple.xml.tmp to simple.xml, and run the processor over it, and viola! we get a nice version number in our documentation. Then if we now rename our older copy of simple.xml.orig to simple.xml, we have the old file back and ready to edit.
Automating the process: Make to the rescue!
Now while the above does work, it is kind of a pain to remember, so let's get even more sophisticated and create a small makefile to do all this for us. Makefile's, for those of you blessed enough to not have encountered this rather cryptic syntax, are a script that you write that defines a series of targets and dependencies, and rules for building each one. You create a Makefile, and then run it through a program called "make". I'll be using the syntax of the more traditional Unix make (specifically that compatible with GNU make program), not the goofier variants espoused by Microsoft or Borland. We could jsut write a script, but using a makefile automates certain parts for us, especially if we want to add other features later on.
Makefiles list each target, followed by a ":", and then a series of 0 or more dependencies, separated by whitespace. The "#" is a single line comment. After a target, you can have a series of one or more rules. Each rule must be preceded by a single TAB character. I repeat, you must precede the rule by a TAB or make will crap out on you. Of course this is a stupid requirement, but that's make for you. Each target can be a file or just a symbolic name. If the target is a file name, make will attempt to determine if it is "up to date" (based on it's dependencies) before going ahead and building the target. If the target is a symbolic name (like ArdVark) it will always get built. So, without further ado, lets create our Makefile like so (make sure the file is just named Makefile):
#Here's out simple makefile
#for the simple documentation
book_ver = `cat version.txt`
docs:
sed "s?BOOK_VERSION?$(book_ver)?g" simple.xml > simple.xml.tmp
cp simple.xml simple.xml.orig
mv simple.xml.tmp simple.xml
xsltproc --nonet simple.xsl simple.xml
-hhc htmlhelp.hhp
mv simple.xml.orig simple.xml
Our Makefile has one variable book_ver
. We can access it's value by using the $(<variable name>) syntax. We have one target "docs" with no dependcies, so it will always be built. If any rule fails (the execution returns a non-zero value) make will stop. If you remember our earlier issue with the HTML Help compiler and it not returning a succesful return code, despite actually creating the correct chm file, you'll see that we use a "-" character before the hhc rule. Using the "-" character tells make to ignore the return code and proceed on, even if the rule "fails".
Save the Makefile and then run make:
make docs
You should see the commands run and at the end of it all, you should end up with a simple.chm with the correct version, as specified in version.txt, and the original simpl.xml should still have it's "BOOK_VERSION" placeholder in it.
We're going to add one final thing to wrap this all up. Wouldn't it be neat if we could make a single file that we could use to hold all the current source documents that we are currently working on? It sure would! So lets add one more target to our make file:
Makefile:
#Here's out simple makefile
#for the simple documentation
book_ver = `cat version.txt`
docs_files = simple.xml \
simple.xsl \
simple.css \
chap1.xml \
chap2.xml \
faqs.xml \
Makefile \
gfx/note.png \
smiley.bmp \
version.txt
docs:
sed "s?BOOK_VERSION?$(book_ver)?g" simple.xml > simple.xml.tmp
cp simple.xml simple.xml.orig
mv simple.xml.tmp simple.xml
xsltproc --nonet simple.xsl simple.xml
-hhc htmlhelp.hhp
mv simple.xml.orig simple.xml
dist:
mkdir simple-$(book_ver)
cp --parents $(docs_files) simple-$(book_ver)/
tar -cf simple-$(book_ver).tar simple-$(book_ver)/
gzip simple-$(book_ver).tar
rm -rf simple-$(book_ver)/
Save this and if you now run:
make dist
You'll end up with the file simple-1.2.3.tar.gz which has all the source to your documentation! Cool.
There's lots more you can do with make, but I'll leave that as an exercise to the reader to play with that. Hopefully this gives you a basic starting point.
Conclusion
Well we have covered a whole lot of ground, and hopefully I didn't lose anyone! We've gone over the basics of DocBook content creation, discussed how to install and process the content to produce html and HTML Help based output. We've customized some of the style sheets for our own use, and written a makefile to support versioning and producing our DocBook generated HTML Help. If you have further questions explore the DocBook links I have provided in the article, feel free to email me, or better yet post a question below. Also, feel free to browse through the VCF's documentation and documentation Makefile. It covers everything in this article plus offers some other, more advanced, customizations.