Chunking customization

Although the chunk stylesheet provides many parameters that control the chunking of your document into multiple HTML files, you may have a need for further customization. If you need to customize how elements are chunked, as opposed to how they look, then you should use version 1.73 or later of the stylesheets. Those templates are cleanly separated, so customization is easier.

If you examine the html/chunk.xsl file from version 1.73 or above, you will see that all it does is load other stylesheet modules.

<xsl:import href="docbook.xsl"/>
<xsl:import href="chunk-common.xsl"/>
<xsl:include href="chunk-code.xsl"/>

The use of both xsl:import and xsl:include, and the order in which they are used, are important for allowing clean customization. It also allows you to create a customized version of the single-page stylesheet as well as the chunking stylesheet using modular files.

Here are the steps for creating customizations of both chunking and single-page stylesheets.

  1. Create a customization file for single-page output as described in the section “Customization layer”. Let's say you name that file mydocbook.xsl. It should contain:

    • An xsl:import of the stock html/docbook.xsl non-chunking stylesheet.

    • Any parameter settings and customizations of how elements should be styled. You can include any parameters that appear in the html/param.xsl stylesheet module, even if they control chunking.

  2. Copy the html/chunk.xsl file to another customization file, such as mychunk.xsl. Edit that file as follows:

    Import your single-page customization file, which imports docbook.xsl:
    <xsl:import href="mydocbook.xsl"/>
    <xsl:import href="chunk-common.xsl"/>
    <xsl:include href="chunk-code.xsl"/>
    Add your parameter settings and customizations that affect chunking behavior here.
    

    Note that chunk-code.xsl is included, not imported.

  3. If you customize any chunking templates with a match attribute (as opposed to a name attribute), then add a priority="1" attribute to the xsl:template element in your customization layer. The next section explains why.

How chunking works

The chunking stylesheet has to process any chunked element in two different ways. A chapter, for example, is always a chunk, and so there must be a template for chapter that generates a new file and adds the correct navigational header and footer. A chapter also has content to be formatted, and so there is another template for chapter that converts its content into HTML elements within the chunk file.

In most situations, processing an element in two different ways is done using a template mode. This does not work for chunking, because of the nature of nested chunks. If you were to create a template for chapter with mode="chunk", then you could use that to create the chunk file and navigational header/footer. Inside that template you just process the chapter in normal mode to format it for HTML. When that normal processing hits a section, it just processes the section as part of the chapter, and does not chunk the section. Somehow the normal mode has to apply-templates to sections in mode="chunk", but the normal mode template has no knowledge of chunking.

So the chunking stylesheet takes a different approach. It operates the two different processes at two different import levels. The chunking stylesheet imports the regular stylesheet, and so the chunking templates have a higher import precedence. There is a chunking template with match="chapter" (in chunk-code.xsl), but it is in normal mode. It is applied first because it has a higher import precedence than the match="chapter" template in the non-chunking stylesheet. It opens a new chunk file and writes the navigational header. Then it uses xsl:apply-imports to format the elements in the chapter. The xsl:apply-imports element ignores matches in the current import level and reaches back to find a match in lower import levels. It finds the match="chapter" template in component.xsl, and uses that to generate HTML for the chapter.

When a section element within a chapter is encountered, the non-chunking template is using an xsl:apply-templates instruction. The processor looks for a match="section" template, and it looks for the match with the highest import precedence. The chunking version of match="section" in chunk-code.xsl has the highest import precedence. This is the case even when the chapter is being processed with a template found via xsl:apply-imports. The import level is not inherited when templates are applied. So the section chunking template starts a new chunk file (while the chapter file is still open for writing), and adds the section navigational header. Then the chunking section template does xsl:apply-imports, and the section is processed with the non-chunking section template to format it. Repeat as needed. When all the section chunks are completed, the non-chunking chapter template runs out of content and finishes. Control returns to the chunking version of the chapter template, which writes the navigational footer and closes the chapter chunk file.

The advantage of this approach is that there is no mode to keep track of, and the original non-chunking templates can be reused without being duplicated (which is a maintenance headache). This approach does require care when customizing, because customization introduces more import levels. The following list describes how the templates are arranged in import levels in a customization, starting with the lowest import precedence. This description applies to version 1.73 and later of the stylesheets.

  1. The original non-chunking templates are at the bottom of the import ladder. If there is no match on an element in the higher import levels, then these are the templates of last resort. In fact, most elements are handled at this level. Only chunked elements and elements you customize are affected by higher import levels.

  2. The next import level up consists of customizations of templates from the non-chunking stylesheet. These are the templates in a non-chunking customization layer such as mydocbook.xsl that imports the base stylesheet. You can use mydocbook.xsl to generate single-file HTML output.

  3. The next import level up consists of the stock chunking templates.

    • All of the chunking templates with a name attribute are in chunk-common.xsl. These are utility templates that are not matched to specific elements.

    • All of the chunking templates with a match attribute are in chunk-code.xsl. These are the templates that initiate chunking for specific elements.

  4. The final import level consists of your customizations of chunking templates. These are in a customization layer such as mychunk.xsl that looks like the following:

    <xsl:import href="mydocbook.xsl"/>
    <xsl:import href="chunk-common.xsl"/>
    <xsl:include href="chunk-code.xsl"/>
    Add your template customizations that affect chunking behavior here.
    
    • The chunk-common.xsl templates are imported. If you customize a named template from chunk-common.xsl, then your template will have higher import precedence than the original imported template.

    • Note that chunk-code.xsl is included, not imported. Because it is included, if you customize a template from chunk-code.xsl, then your customization has the same import precedence as the original chunking template. That's why you must add a priority="1" attribute to your customized match template. If you do not, then the XSL processor will complain that there are two templates that match on one element. Adding the priority attribute resolves the conflict.

Why use xsl:include instead of xsl:import for chunk-code.xsl? The reason is the use of xsl:apply-imports in those templates. It must maintain a single level of import level difference in order to reach the non-chunking template when xsl:apply-imports is used.

If you were to use xsl:import on chunk-code.xsl, and then customize one of those templates, here is what would happen:

  1. Your customized chunking template initiates a chunk file for an element such as chapter.

  2. Then your customized chunking template uses xsl:apply-imports to format the content of the chapter.

  3. The processor looks for a template with match="chapter" below the current import level.

  4. It finds the template with match="chapter" in chunk-code.xsl, which is imported. But that is the original chunking chapter template, not the non-chunking version that you need.

  5. The original chunking chapter template starts a chunk file of the same name, and then it also does xsl:apply-imports. This time the non-chunking chapter template is found, and it formats the chapter content into the file.

  6. The original chunking chapter template closes the file and returns control to the customized chunking chapter template.

  7. The customized chunking chapter template then writes the footer and then writes the file. Because it is the same filename, it overwrites the previous file. You are left with a chunk that has the correct filename and the correct header and footer, but no content. Its content was separately chunked into a file of the same name that was just overwritten.

When xsl:include is used for chunk-code.xsl, then no new import level is introduced for those templates. When xsl:apply-imports is used, in either the original or a customized version, then it finds the non-chunking template to fill in the content.

Chunking templates

Even with this arrangement, customization of the chunking behavior will require you to understand the templates that perform the chunking process. The behavior is further complicated by options such as fast chunking (when using chunkfast.xsl instead of chunk.xsl) and the onechunk.xsl variation. The following table summarizes the important chunking templates (as of version 1.73 and later).

Table 12.3. Chunking templates

TemplateLocationFunction
<xsl:template match="/">chunk-code.xslMatches document root to start processing.
<xsl:template 
   match="set|book|part|...
chunk-code.xslMatches elements that are always chunks and calls the process-chunk-element template.
<xsl:template 
   match="sect1|sect2|...
chunk-code.xslMatches section elements, calls the chunk template to determine if it is to be chunked, and then calls the process-chunk-element if it is.
<xsl:template 
   name="chunk">
chunk-common.xslDetermines if the current element is a chunk. It returns 1 if the element is a chunk, or 0 otherwise.
<xsl:template
   name="process-chunk-element">
chunk-common.xslApplies templates to style the element content and saves the styled result in a variable. Routes the content to the appropriate template based on chunking options that are set.
<xsl:template 
   name="chunk-all-sections">
and others
chunk-common.xslDetermines the Next and Previous links for the given chunk element and calls the process-chunk template.
<xsl:template 
   name="process-chunk">
chunk-common.xslDetermines the filename of the chunk and calls the write-chunk template.
<xsl:template 
   name="write.chunk">
chunker.xslCalls the appropriate extension function for the current processor to write the styled content to a chunk file.

When customizing any of these templates, be sure to set up your chunking customization using the guidelines in the section “Chunking customization”.