Search code examples
xsltxsl-fodocbookdocbook-5

Styling part of a docbook procedure using XSL


I'm trying to affect the properties of the fo:list-block within a docbook 5 procedure. What I want to do is take the following docbook XML code:

<procedure>
    <title>Eating a banana</title>
    <step><para>Find banana</para></step>
    <step><para>Peel banana</para></step>
    <step><para>Stick banana in mouth</para></step>
<procedure>

and affect the FO output of just the steps (a list), not the title.

Using this:

<xsl:template match="d:procedure">
    <fo:block border-left-width="1pt" border-left-style="solid" padding-left="0.25in">
        <xsl:apply-imports />
    </fo:block>
</xsl:template>

Gets me something like this:

|
| Procedure 1: Eating a banana
|   1. Find banana
|   2. Peal banana
|   3. Stick banana in mouth
|

What I'm trying to get is:

Procedure 1: Eating a banana
  |   1. Find banana
  |   2. Peal banana
  |   3. Stick banana in mouth

Trying to match to <step> is invalid, since docbook turns those into fo:list-block (fo:block as a descendant of fo:list-block generates an "invalid child" error).

Docbook has a xsl:attribute-set for <procedures>, but as far as I can tell, that can only be used to style the entire block (similar to the template match='d:procedure', not just the list.


Solution

  • Here is how it can be done.

    1. Add this version of the "procedure" template (the original is in lists.xsl) to your customization layer:

      <xsl:template match="d:procedure">
        <xsl:variable name="id">
          <xsl:call-template name="object.id"/>
        </xsl:variable>
      
        <!-- Preserve order of PIs and comments -->
        <xsl:variable name="preamble"
              select="*[not(self::d:step
                        or self::d:title
                        or self::d:titleabbrev)]
                      |comment()[not(preceding-sibling::d:step)]
                      |processing-instruction()[not(preceding-sibling::d:step)]"/>
      
        <xsl:variable name="steps" 
                      select="d:step
                              |comment()[preceding-sibling::d:step]
                              |processing-instruction()[preceding-sibling::d:step]"/>
      
        <xsl:call-template name="formal.object.heading"/>  
      
        <fo:block id="{$id}" xsl:use-attribute-sets="procedure.properties list.block.spacing"
              border-left-width="1pt" border-left-style="solid" padding-left="0.25in">
      
          <xsl:apply-templates select="$preamble"/>
      
          <fo:list-block xsl:use-attribute-sets="list.block.spacing"
                         provisional-distance-between-starts="2em"
                         provisional-label-separation="0.2em">
            <xsl:apply-templates select="$steps"/>
          </fo:list-block>
      
        </fo:block>
      </xsl:template>
      

      <xsl:call-template name="formal.object.heading"/> (which returns a fo:block with the procedure title) is executed before the block with the border is being output. I have also simplified the template by removing code that handles placement of the title.

    2. Add text-indent to the formal.title.properties attribute-set:

      <xsl:attribute-set name="formal.title.properties" 
                     use-attribute-sets="normal.para.spacing">
        <xsl:attribute name="text-indent">
          <xsl:choose>
            <xsl:when test="self::d:procedure">-30pt</xsl:when>
            <xsl:otherwise>0pt</xsl:otherwise>
          </xsl:choose>
        </xsl:attribute>
      </xsl:attribute-set>
      

    This will push the procedure title to the left (but leave other titles alone).