Chapter 5Implementation details

This section sketches out some features of the implementation. It would probably be better to build an annotated Definitive Guide or something, but this will have to do for now.

5.1Customizing chunking

Chunking is controlled by the $chunk-include and $chunk-exclude parameters. These parameters are both strings that must contain an XPath expression.

For each node in the document, the $chunk-include parameter is evaluated. If it does not return an empty sequence, the element is considered a chunking candidate. In this case, the $chunk-exclude parameter is evaluated. If the exclude expression does return an empty sequence, then the element identified becomes a chunk. (If the exclude expression returns a non-empty value, the element will not become a chunk.)

5.2Lengths and units

Lengths appear in the context of images (width and height) and tables (column widths). Several different units of length are possible: absolute lengths (e.g., 3in), relative lengths (e.g., 3*), and percentages (e.g., 25%). In some contexts, these can be combined: a column width of “3*+0.5in” should have a width equal to 3 times the relative width plus ½ inch.

In practice, some of the more complicated forms in TR 9502:1995 have no direct mapping to the units available in HTML and CSS. The stylesheets attempt to specify a mapping that’s close. Broadly, they take the nominal width of the table ($nominal-page-width), subtract out the fixed widths, divide up the remaining widths proportionally among the relative widths, and compute final widths. The final widths can be expressed either in absolute terms or as percentages.

In handling the width and height of images, the intrinsic width and height of the image in pixels are converted into lengths by dividing by $pixels-per-inch. Nominal widths are taken into consideration if necessary.

Note

Determining the intrinsic size of an image depends on an extension function. See Section 2.6, “Extension functions”. Many bitmap image formats are supported. The bounding box of EPS images is used, if it’s present. The intrinsic size of SVG images is not available.

The list of recognized units (in, cm, etc.) are taken from $v:unit-scale.

5.3Line numbers

In the lines and table styles, line numbers may be added to the beginning of some (or all) lines. Prior to version 1.10.0, the stylesheets inserted the numbers without any padding:

  |<span class="line">
  |  <span class="ln">5</span>
  |  <span class="ld">The line of text</span>
  |</span>

(The newlines and indentation in these examples are for clarity. In practice, these are inside a pre where every space counts and they’re all run together with line breaks only occurring between lines.)

In a graphical browser with CSS support, this looked fine. But without CSS, the line numbers and the text that followed them could flow together and the alignment of the numbers was unclear.

Starting in version 1.10.0, the stylesheets insert padding spaces before each number so that they will all be aligned. If the largest line number is three digits long, every number smaller than 100 will be padded to a width of three characters. A single space is added after the number to separate it from the text that follows. An additional separator may also be inserted, as shown here.

  |<span class="line">
  |  <span class="ln"> 5 <span class="nsep">|</span></span>
  |  <span class="ld">The line of text</span>
  |</span>

These changes have no visible effect when CSS is used to style the verbatim environment. But without CSS, the numbers are aligned and separated from the text that follows. The $verbatim-number-separator is generally suppressed by CSS, but is visible in text browsers.

5.4Processing mediaobjects

Starting in version 1.11.0, the way media objects are processed has been refactored. This is designed to support fallback at both the object level (imageobject, audioaobject, videoobject, textobject, and imageobjectco) and at the data level (imagedata, audiodata, videodata, and textdata within the objects).

Each data element and object element is processed in the m:mediaobject-info mode. This returns a map for each object that contains an array of maps, one for each data element:

Table 5.1The object map
KeyValue
content-typesAn array of the distinct content types in the data elements
datasAn array of data maps
extensionsAn array of the distinct extensions in the data elements
nodeThe media object node

Each data map has the following structure:

Table 5.2The data map
KeyValue
alignThe alignment of the data (if specified)
content-typeThe computed content type for the data
contentheightThe content height of the data (if specifieda)
contentwidthThe content width of the data (if specified)
extensionThe extension of the data file (if there is one)
filerefThe original fileref attribute value
heightThe height of the data (if specifieda)
hrefThe computed href value for the HTML element; this takes account of the $mediaobject-input-base-uri and $mediaobject-output-base-uri).
nodeThe data element
paramsAny multimediaparams associated with the data element
propertiesThe properties of the element (as returned by the extension funtions; this can include EXIF data and metadata)
scaleThe scaling factor (if there is one) or 1.0
scalefitTrue if the image should be scaled to fit (implicitly or explicitly)
uriThe computed absolute URI of the input data
valignThe vertical alignment of the data (if specified)
widthThe width of the data (if specifieid)

DocBook uses “depth” instead of “height”, but we convert it to height for consistency with most other systems.

The uri and href properties are computed by processing the data elements in the m:mediaobject-uris mode.

Armed with information about the objects and the data associated with them, the stylesheets proceed to choose an object and then process it. Each object is considered in turn, if any of the data elements it contains were excluded, then it is rejected. The first object where all of the elements are acceptable is selected.

Consider this example:

 1 |<mediaobject>
   |<imageobject>
   |  <imagedata fileref="image.svg"/>
   |  <imagedata fileref="image.eps"/>
 5 |  <imagedata fileref="image.png" width="4in"/>
   |</imageobject>
   |<imageobject>
   |  <imagedata fileref="image.svg"/>
   |  <imagedata fileref="image.png" width="40em"/>
10 |</imageobject>
   |</mediaobject>

If this is being processed for online presentation, the default value of $mediaobject-exclude-extensions will exclude the EPS file. Because one of it’s data elements was excluded, the processor will choose the object containing only the SVG and PNG images for online presentation.

Once an object is selected, an appropriate wrapper is created and all of the alternatives are placed within it. So the example above will result in picture element containing a source for the SVG image and an img for the fallback PNG.

Note

Consistent with HTML, only the size, scaling, and alignment attributes of the last alternative data element are considered! These apply irrespective of which alternative is selected.

5.4.1Mediaobject URIs

Media object URIs are tricky to handle. It’s most convenient if the URIs in the source documents point to the actual media files. This allows extensions, like the image properties extension function, to access the files. At the same time, the references generated in the HTML have to point to the locations where they will be published.

In previous versions, the stylesheets attempted (broadly) to use the relative difference between the input and output base URIs to work out the correct relative URIs for media. That imposed restrictions on the authoring environment that weren’t always easy to work with. Starting in version 2.0.6, the mechanisms for finding sources and producing references in the output has changed. Three parameters are used:

$mediaobject-input-base-uri

If the $mediaobject-input-base-uri is empty (the default), then URIs in the source document are assumed to be relative to the base URI on which they occur. This is the usual case if you mix XML and media into the same directory structure on the filesystem.

If the $mediaobject-input-base-uri is not empty, it is used to resolve all media URIs. If it’s initialized with a relative URI, that URI will be made absolute against the base URI if the input document.

$mediaobject-output-base-uri

If the $mediaobject-output-base-uri is empty (the default), then URIs in the output are treated as parallel to the URIs in the input. If the reference ../image.png works in the source document, it’s assumed that will also work in the output document.

If the $mediaobject-output-base-uri is not empty, it is the base URI used for media objects. If this is a relative URI, it is taken to be relative to the root of the output hierarchy.

Suppose the output base URI is https://images.example.com/, then a reference to image.png will appear as https://images.example.com/image.png in the output.

If the output base URI is media/, then a reference to image.png will appear as media/image.png in the output. If the document is chunked, the paths back to the output directory are relative. In otherwords, if the reference to image.png appears in a chunk that will be located at back/appendix.html, then the media URI will be ../media/image.png.

$mediaobject-output-paths

This parameter controls whether the relative paths in the input URIs apply to the output URIs as well. If the parameter is true, the output base URI is media/, and the input URI is path/to/image.png, then the output reference will be to media/path/to/image.png. If it’s false, then the output reference will be to media/image.png.

For a further discussion of the options, see Section 3.5, “Managing media”.

Important

The stylesheets are not responsible for actually copying the media files into the correct locations in the output. The stylesheets only generate the HTML files and the references. You must copy the images and other media with some other process.

5.5Templates

It’s difficult to make title pages for documents easy to customize. There is a lot of variation between documents and the styles can have very precise design constraints. At the end of the day, if you need complete control, you can define a template that matches the element in the m:generate-titlepage mode and generate all of the markup you wish.

The default title page handling attempts to make some declarative customization possible by using templates. A typical header template looks like this:

 1 |<db:section>
   |  <header>
   |    <tmp:apply-templates select="db:title">
   |      <h2><tmp:content/></h2>
 5 |    </tmp:apply-templates>
   |    <tmp:apply-templates select="db:subtitle">
   |      <h3><tmp:content/></h3>
   |    </tmp:apply-templates>
   |  </header>
10 |</db:section>

Any HTML element in the template will be copied to the output. The semantics of a “template apply templates” element (tmp:apply-templates) is that it runs the ordinary xsl:apply-templates instruction on the elements that match its select expression. If they result is the empty sequence (e.g, if there is no subtitle), nothing is output. If there is a result, the content of the tmp:apply-templates element is processed. Anywhere that tmp:content appears, the result of applying templates will be output.

In this example, if the title is “H2O” and there is no subtitle, the resulting HTML title page will be:

  |<header>
  |  <h2>H<sub>2</sub>O</h2>
  |</header>

5.6Annotations

The stylesheets fully support annotations, including a number of presentation styles enabled by JavaScript in the browser. They also support an extension of the documented semantics of annotation.

Annotations are applied to elements with links. Either the element must point to its annotations (with an annotations attribute) or the annotations must point to the elements they annotate (with an annotates attribute). These are documented as ID/IDREF links but they are not IDREFS attributes because annotations may be stored separately.

Starting in version 1.5.1, the DocBook xslTNG Stylesheets1 support a non-standard extension: if you place the string xpath: in the annotates attribute of an annotation, then the rest of the attribute is assumed to contain an XPath expression that points to the element(s) to which the annotation applies. (If you want to put IDREF values before the xpath: token, that’s fine, but you can’t put them after; the expression continues to the end of the attribute value.)

Suppose, for example, that you wanted to annotate the stylesheet title in the previous paragraph. The standard mechanisms would require that you either put an xml:id attribute on the element or point to the annotation from the element. With the XPath extension, you can do this:

1 |<annotation
  |   annotates="xpath:preceding-sibling::db:para/db:citetitle"
  |   xmlns:db="http://docbook.org/ns/docbook">
  |<para>This annotation applies to the stylesheet title.
5 |For a discussion of this annotation, see the
  |following paragraphs.</para>
  |</annotation>

When the XPath expression is evaluated, the annotation element is the context item. Often, this means that you’ll want to start the expression with id() or /.

The namespace context for the expression is also the annotation element, that’s why I’ve added the DocBook namespace binding for the db prefix. In practice, if you’re doing this on several annotations, you can just put all the namespace bindings on a common ancestor. All of the bindings in scope on the annotation element are available in the expression.

Caveats:

  1. There’s no way to have multiple XPath expressions. You can’t put “xpath:” in there twice. If you want an annotation to apply to multiple elements, you’ll have to construct a single expression that selects all the elements, or duplicate the annotation, or use ID/IDREF links.

    If this turns out to be a serious limitation in practice, additional syntax could be added to support multiple expressions, but it doesn’t seem necessary.

  2. You can only select elements. There’s no way to select the third word in a particular paragraph, for example, unless it already has some markup around it. There’s also no way to select a comment or a processing instruction.

The placement of the annotation marker (“⌖” by default) can also be controlled globally and on individual annotations. The $annotation-placement parameter provides global control. To specify the position for an individual annotation, put the token “before” or “after” in the role attribute on the annotation.

5.7The pre- and post-processing pipeline

Processing a DocBook document is a multi-stage process. The original document is transformed several times before converting it to HTML. The standard transformations are:

  1. Adjust the logical structure. Adds an XML base attribute to the root of the document and converts media object entityref attributes into fileref attributes.

  2. Perform XInclude processing. Only occurs if the appropriate extension function is available and if the document contains XInclude element.

  3. Convert DocBook 4.x to DocBook 5.x. Only occurs if the root element is not in a namespace.

  4. Peform transclusion.

  5. Perform profiling.

  6. Normalize the content. This removes a lot of variation that’s allowed for authoring. For example, authors aren’t required to use an info element if a formal object has only a title. This process adds the info element if it’s missing.

  7. Resolve annotations.

  8. Process XLink link bases.

  9. Validate. Only occurs if the appropriate extension function is available and the stylesheet specifies a $relax-ng-grammar.

  10. Process Oxygen change markup. Only occurs if $oxy-markup is true and the document contains Oxygen change markup processing instructions.

A customization can introduce transformations to the original document using three parameters:

$transform-original

This transform runs before step 1 in the standard transformations. Note that this is before XInclude processing, before transclusion, before any other processing. If you need to make a change to the original input document, this is where you can do it, but for preprocessing “the XML document that will be transformed”, the $transform-before parameter is likely to be a better choice.

If this transformation is used, it must take special care to preserve the base URI of the original document by adding an xml:base attribute to the root element (if it doesn’t already have one).

Only the first transformation in the list has access to the original base URI. If it isn’t preserved, relative references to other documents will be resolved against the static base URI of the stylesheet and not the URI of the original document. That’s unlikely to be correct.

$transform-before

This transformation runs after step 10. Its input is the DocBook document that will be transformed into HTML.

$transform-after

This transformation runs after the DocBook document has been transformed into HTML. The resulting HTML document is not valid HTML, but contains islands of valid HTML that will be separated out into chunks by subsequent processing.

(If you need to insert a transformation in the middle of the standard transformations, you’ll have to update the $v:standard-transforms variable in your customization.)

Each of the transformation variables holds a list of transforms that will be applied in the order specified. Each member of the list can be a map or a string. If a string is provided, it’s the equivalent of providing this map:

  |map {
  |  'stylesheet-location': $the-string
  |}

The map can have several keys:

Table 5.3The transformation map
KeyValue
stylesheet-locationThe location of the XSLT stylesheet that performs this transformation. This key is required.
extra-paramsA map of QName/value pairs. These parameters will be made available to the transformation in addition to all of the standard parameters available to a standard DocBook stylesheet.
functionsA list of functions (expressed as EQNames). The transformation will only be run if every extension function listed is available.
testAn arbitrary XPath expression. The expression will be evaluated with the document as the context item. If it returns an effective boolean value of true, the transformation will be run.
Annotations
1

This annotation applies to the stylesheet title. For a discussion of this annotation, see the following paragraphs.