Skip to content

DITA XML and XSL-FO tutorials

The following tutorials will help the technical writer set up and use a free DITA XML authoring and publishing chain. For a broader overview of structured and unstructured formats, including the rationale for choosing DITA XML, see the dedicated article.

DITA XML is a structured authoring language that allows you to create documents without worrying about their final appearance on different media. XSL-FO is a language for reorganizing and filtering XML content and applying a style sheet layout to it.

A set of DITA XML files contains all the content related, for example, to a product. Various XSL-FO style sheets are used to publish this content in PDF, HTML, or other formats, applying complex transformations. The summary of each section of the final document may, for example, appear in the HTML version but not in the PDF version.

Similarly, if a product is to be supplied as a white label to different customers, a completely different layout can be applied to its documentation simply by specifying a different set of style sheets when generating the deliverable. In practice, this is not possible with traditional FrameMaker solutions.

XSL-FO: Filter content according to “except” and “or” conditions

Section titled “XSL-FO: Filter content according to “except” and “or” conditions”

Suppose you want to filter the child nodes of the DITA XML tag <example> and display all its content except the title (located between the <title> tags).

You can use the following syntax:

<xsl:template match="*[contains(@class,' topic/example ')]">
<fo:block>
<xsl:apply-templates select="*[not(name()='title')]" />
</fo:block>
</xsl:template>

This command selects all child nodes of the <example> node, excluding the <title> node. However, the <example> node accepts text entered directly, without being encapsulated in tags. Therefore, this command will not display this content.

Suppose the source code of one of your DITA XML files is as follows:

<example>
<title>
XSL-FO
</title>
Here is my XPATH path example:
<codeblock>
ancestor-or-self
</codeblock>
Unencapsulated text located after a child node.
</example>

The PDF file will display the example structured as follows:

ancestor-or-self

The title of the example is not displayed, which is the desired result, but content not encapsulated in tags is not displayed, which is an undesirable side effect. To select this content, text nodes must be selected using the text() syntax. You may then be tempted to use the following syntax:

<xsl:template match="*[contains(@class,' topic/example ')]">
<fo:block>
<xsl:apply-templates select="text()" />
<xsl:apply-templates select="*[not(name()='title')]" />
</fo:block>
</xsl:template>

However, all text elements not encapsulated in child tags of the <example> tag will be placed at the beginning of the example, before the encapsulated elements, even if they are placed afterward in the DITA XML source file.

The PDF file will display the example structured as follows:

Here’s my XPATH path example: Unencapsulated text after a child node.

ancestor-or-self

You must then use the pipe syntax (Boolean or condition) to modify the XPATH path as follows:

<xsl:apply-templates select="text()|*[not(name()='title')]" />

The final result will be:

<xsl:template match="*[contains(@class,' topic/example ')]">
<fo:block>
<xsl:apply-templates select="text()|*[not(name()='title')]" />
</fo:block>
</xsl:template>

The PDF file will display the example structured as follows:

Here is my XPATH path example:

ancestor-or-self

Unencapsulated text after a child node.

XSL-FO: Automatically insert a title for examples

Section titled “XSL-FO: Automatically insert a title for examples”

By default, DITA-OT does not automatically insert the text Example: before the title of an example contained between DITA XML tags <example>. However, XSL-FO syntax offers this possibility.

Suppose the source code of one of your DITA XML files is as follows:

<example>
<title>
XSL-FO
</title>
Here is my XPATH path example:
<codeblock>
ancestor-or-self
</codeblock>
</example>

You want the generated PDF file to display the example structured as follows:

Example: XSL-FO

Here’s my XPATH path example:

ancestor-or-self

and if the example does not contain a title, it should be structured as follows:

Example:

Here’s my XPATH path example:

ancestor-or-self

By default, however, this content will be structured in the PDF by DITA-OT as:

XSL-FO

Here is my XPATH path example:

ancestor-or-self

It’s possible to enter text between <example> tags, but XSL-FO offers a more elegant and structured method.

Automatically insert a text variable before the title of examples

Section titled “Automatically insert a text variable before the title of examples”
  1. Replace in the plugins/org.dita.pdf2/xsl/fo/commons.xsl stylesheet (under DITA-OT 1.7.) the following template:

    <xsl:template match="*[contains(@class,' topic/example')]/*
    [contains(@class,' topic/title ')]">
    <fo:block xsl:use-attribute-sets="example.title">
    <xsl:call-template name="commonattributes"/>
    <xsl:apply-templates/>
    </fo:block>
    </xsl:template>

    with the following code:

    <xsl:template match="*[contains(@class,' topic/example ')]">
    <fo:block xsl:use-attribute-sets="example.title">
    <xsl:call-template name="insertVariable">
    <xsl:with-param name="theVariableID"
    select="'my-example-text'"/>
    </xsl:call-template>
    <xsl:apply-templates select="title"/>
    </fo:block>
    <fo:block>
    <xsl:apply-templates
    select="*[not(contains(@class, ' topic/title'))]
    |text()|processing-instruction()"/>
    </fo:block>
    </xsl:template>
  2. Define in the files containing the language variables, such as plugins/org.dita.pdf2/cfg/common/vars/en.xml, the text variables to be inserted automatically, for example:

    <variable id="my-example-text">Example:</variable>

To achieve consistent behavior, you should disable this treatment for examples of specific topics types (task, in particular).

Generate a PDF with DITA Open Toolkit under GNU/Linux

Section titled “Generate a PDF with DITA Open Toolkit under GNU/Linux”

This DITA XML tutorial is designed to guide you through setting up and using the DITA-OT publishing chain in a GNU/Linux environment (Ubuntu or Debian).

Prerequisites

  • Ubuntu or Debian on a physical or virtual machine with administrator access
  • Java 17 or later (JDK)
  • Internet connection
  1. Download and unzip the DITA-OT archive:

    Terminal window
    export REPO="https://github.com/dita-ot/dita-ot"
    wget $REPO/releases/download/4.4/dita-ot-4.4.zip
    unzip dita-ot-4.4.zip
  2. Generate your first PDF:

    Terminal window
    cd dita-ot-4.4
    bin/dita --input=samples/taskbook.ditamap --format=pdf

Congratulations, you’ve compiled your first DITA XML project! The PDF file generated is out/taskbook.pdf. You can now compile other projects by skipping steps 1 and 2.

Generate a PDF with DITA Open Toolkit (Windows)

Section titled “Generate a PDF with DITA Open Toolkit (Windows)”

This DITA XML tutorial is designed to guide you through setting up and using the DITA-OT publishing chain on a current Windows installation.

Prerequisites

  • Java 17 or later (JDK)
  • Internet connection
  1. Download Java, then run the installation program.

  2. Download DITA Open Toolkit 4.4 (dita-ot-4.4.zip), then extract it, for example to your desktop.

  3. Open the Start menu, type cmd, then press Enter. A terminal appears.

  4. In the terminal, go to the extracted directory:

    Terminal window
    cd Desktop\dita-ot-4.4
  5. Generate your first PDF:

    Terminal window
    bin\dita.bat --input=samples\taskbook.ditamap --format=pdf

    This command generates a PDF file from a sample DITA XML project.

    Congratulations, you’ve compiled your first DITA XML project! You’ll find the target file in the out directory. You can now compile other projects by skipping steps 1 and 2.

Manage multilingual DITA XML documentation projects

Section titled “Manage multilingual DITA XML documentation projects”

DITA XML is a great format for managing documentation projects. For multilingual projects, however, the technical writer must create a ditamap file, which contains the table-of-contents structure of the documents, by version. This creates the risk of errors and inconsistencies. Fortunately, an appropriate methodology and an automation script for the DITA-OT publishing chain remedy this problem. The modular document base article explains how DITA XML content is organized for multi-language reuse.

Methodology for multilingual DITA XML documentation projects

Section titled “Methodology for multilingual DITA XML documentation projects”
  1. The ditamap file should not include a navtitle section, which contains a spelled-out title instead of extracting the title from the corresponding DITA XML section, and is therefore language-specific.

  2. From the outset of your DITA XML project, place DITA XML content files in a subdirectory specific to the language in which they are initially written.

    For example:

    • product

      • en_US
        • images
        • tasks
        • topics

      and not:

    • product

      • images
      • tasks
      • topics
  3. Replace all occurrences of the language-specific directory name in the ditamap file with a single temporary string.

    For example, use the string @language-code@:

    <topicref href="@language-code@/topics/managing-rights.dita"/>

    and not:

    <topicref href="en_US/topics/managing-rights.dita"/>
  4. To generate the target files, you can now:

    a. Modify the default.locale parameter in the demo/fo/build.xml file. b. Replace the language variable in the ditamap file with the name of the language directory. c. Change the language parameter xml:lang in the ditamap file and in DITA XML content files. d. For PDF target files, modify page dimensions (A4 or US letter, for example) according to language. e. Generate target files. f. Restore initial values in source files.

Fortunately, a simple Bash script (GNU/Linux) can automate all this.

Prerequisites

  • You have installed DITA-OT.
  • Your DITA XML project includes only one ditamap file.
  • Your DITA XML content files have the extension .dita.
  • The directory names of the language versions correspond to the language codes supported by Dita Open Toolkit (fr_FR or en_US, for example).
  • Your DITA XML content files are located in subdirectories of the language version directories (for example, in fr_FR/tasks/ and fr_FR/topics/).

Supported values for PDF page size are fr_FR (A4) and en_US (US letter). This script can of course be easily adapted or inspire a new script.

To use this script:

  1. Download the multilingual DITA XML generation script into the directory containing the project ditamap file.

  2. In a terminal, navigate to this directory and enter:

    Terminal window
    chmod +x dita2target.sh
  3. In the terminal, enter:

    Terminal window
    mkdir out

    to create the directory containing the target files.

  4. Enter:

    Terminal window
    ./dita2target.sh <ditamap file> \
    <language directory name> <target format>

    to generate target files.

    The target format argument accepts values managed by DITA-OT.

    Example

    Terminal window
    ./dita2target.sh firewall.ditamap en_US pdf2

    The PDF file firewall.pdf is then generated in the out directory (hard-coded in the script).

Create different documents from the same DITA sources

Section titled “Create different documents from the same DITA sources”

DITA XML offers a conditional text mechanism. This mechanism promotes the reuse of source content and avoids redundant information. This tutorial will help the technical writer use this mechanism in just a few minutes.

Prerequisites

  • You have installed DITA-OT in the DITA-OT1.5.4 directory under GNU/Linux or Windows.
  1. Paste the following code into a file and save it as conditional-text.dita in the DITA-OT1.5.4 directory:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA 1.2 Topic//EN"
    "/usr/share/dita-ot/dtd/technicalContent/dtd/topic.dtd">
    <topic id="example-topic" xml:lang="en-us">
    <title>Using Conditional Text</title>
    <body>
    <hazardstatement>
    <messagepanel audience="electricians">
    <typeofhazard>
    Danger for electricians
    </typeofhazard>
    <consequence>
    Risk of electrocution
    </consequence>
    <howtoavoid>
    Do not touch electrical wires.
    </howtoavoid>
    </messagepanel>
    <messagepanel audience="plumbers">
    <typeofhazard>
    Danger for plumbers
    </typeofhazard>
    <consequence>
    Risk of drowning
    </consequence>
    <howtoavoid>
    Do not dive into the pool.
    </howtoavoid>
    </messagepanel>
    </hazardstatement>
    <p>
    Any content placed between tags that does not include an
    <i>audience</i> value excluded in a file
    <i>.ditaval</i> is published in documents
    intended for plumbers and electricians.
    </p>
    </body>
    </topic>

    This code contains DITA XML tags containing different audience values: we’ll exclude the contents of one of these two tags when generating the target file using the audience key.

  2. Paste the following code into a file and save it as conditional-text.ditamap in the DITA-OT1.5.4 directory:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE bookmap PUBLIC "-//OASIS//DTD DITA BookMap//EN"
    "/usr/share/dita-ot/dtd/bookmap/dtd/bookmap.dtd">
    <bookmap id="conditional-text">
    <booktitle>
    <mainbooktitle>
    Example of conditional text
    </mainbooktitle>
    </booktitle>
    <chapter href="conditional-text.dita"/>
    </bookmap>
  3. Paste the following code into a file and save it as electricians.ditaval in the DITA-OT1.5.4 directory:

    <?xml version="1.0" encoding="UTF-8"?>
    <val>
    <prop att="audience" val="electricians" action="include"/>
    <prop att="audience" val="plumbers" action="exclude"/>
    </val>
  4. Paste the following code into a file and save it as plumbers.ditaval in the DITA-OT1.5.4 directory:

    <?xml version="1.0" encoding="UTF-8"?>
    <val>
    <prop att="audience" val="electricians" action="exclude"/>
    <prop att="audience" val="plumbers" action="include"/>
    </val>
  5. Open a terminal and enter the following command in the DITA-OT1.5.4 directory:

    Terminal window
    java -jar lib/dost.jar /i:conditional-text.ditamap \
    /filter:electricians.ditaval /outdir:. /transtype:pdf2

    Open the file conditional-text.pdf; it contains information for:

    • plumbers and electricians,
    • electricians only.
  6. Open a terminal and enter the following command in the DITA-OT1.5.4 directory:

    Terminal window
    java -jar lib/dost.jar /i:conditional-text.ditamap \
    /filter:plumbers.ditaval /outdir:. /transtype:pdf2

    Open the file conditional-text.pdf; it contains information for:

    • plumbers and electricians,
    • plumbers only.

DITA Open Toolkit: Display cross-references in PDFs

Section titled “DITA Open Toolkit: Display cross-references in PDFs”

You have placed correctly formatted related-links tags in your DITA XML content files, or better still, a reltable in your ditamap table of contents structure (the reltable allows you to decontextualize your content and therefore reuse it better). You launch your PDF generation command and, unpleasantly, no Additional resources section appears in the target file! You then try to generate an HTML version of your content and there, your See also section is indeed present. Wouldn’t DITA-OT support cross-referencing in PDFs?

Fortunately, no. By default, cross-references are not generated in PDFs by DITA-OT. To display them, assign the value no to the disableRelatedLinks variable in the demo/fo/build_template.xml file. If you use ant, you’ll also need to pass the args.fo.include.rellinks=all parameter as follows:

Terminal window
ant -Dargs.input=samples/sequence.ditamap -Doutput.dir=out/ \
-Dtranstype=pdf2 -Dargs.fo.include.rellinks=all

You’ve meticulously inserted your index entries into your DITA XML content files and you want them to appear in the generated PDF.

Historically, the Apache FOP processor bundled with DITA-OT did not support index generation, so displaying an index required a commercial FO processor (such as RenderX’s XEP, used by proprietary tools like XMetaL) or one of several workarounds. This is no longer the case: index generation was reworked in DITA-OT 2.2 and the bundled Apache FOP now renders indexes directly. With a current DITA-OT release, the index entries you insert in your DITA XML content are included in the PDF output - no extra processor or workaround is required.

If you nonetheless choose not to display an index in the PDF, that remains a legitimate option: indexes are difficult to maintain and offer a questionable surplus of usability in a document that will only be consulted marginally in printed form. And for electronic formats, the absence of an index is more than compensated for by the full-text search function.

The nXML mode offers real-time validation of XML documents (DocBook, XHTML, and others) and context-sensitive autocompletion of XML tags - no need to know the schema by heart. It has been built into Emacs core since Emacs 23, so there is nothing to install: any current Emacs already includes it. Out of the box, however, nXML mode doesn’t know about DITA XML; this tutorial shows you how to enable it for DITA XML by associating the DITA RelaxNG schemas.

Prerequisites

  • Emacs 23 or later (any current version; nXML mode is included)

  • The directory structure of your DITA XML documentation project must be as follows:

    • language directory
      • concepts
      • faq
      • reference
      • tasks
      • topics

    where <language directory> has the value en_US, or fr_FR, etc.

  • Command-line instructions are designed for GNU/Linux; they must be adapted for use in other environments.

  1. Make a backup of your entire DITA XML documentation project.

  2. nXML mode is already part of Emacs, so there is nothing to download or patch. It is the default mode for .xml files; to use it for DITA content files as well, tell Emacs to open .dita files in nXML mode by adding the following line to your .emacs (or init.el):

    (add-to-list 'auto-mode-alist '("\\.dita\\'" . nxml-mode))
  3. Download the RelaxNG schema archive for DITA XML into the root directory of your DITA XML documentation project.

  4. Go to the root directory of your DITA XML documentation project, then paste the following command:

    Terminal window
    tar xzvf rnc.tar.gz

    This command creates an rnc directory at the same level as the <language directory>.

  5. Download the archive of schemas.xml files into the root directory of your DITA XML documentation project, then paste the command sequence below, replacing <language directory> with the appropriate value, such as en_US, or fr_FR. Repeat this step for all your language directories.

    Terminal window
    export DIR="schemas.redaction-technique.org"
    tar xzvf $DIR.tar.gz && \
    cd <language directory> && \
    cp ../$DIR/concepts/schemas.xml concepts/ && \
    cp ../$DIR/faq/schemas.xml faq/ && \
    cp ../$DIR/reference/schemas.xml reference/ && \
    cp ../$DIR/tasks/schemas.xml tasks/ && \
    cp ../$DIR/topics/schemas.xml topics/ && \
    rm -rf ../$DIR/

    Your language directories should now contain the appropriate schemas.xml files:

    • en_US
      • concepts
        • schemas.xml
      • faq
        • schemas.xml
      • reference
        • schemas.xml
      • tasks
        • schemas.xml
      • topics
        • schemas.xml

    These schemas.xml files are nXML mode’s schema-locating files: they tell nXML which RelaxNG schema to apply to each DITA topic type, which is what enables validation and tag completion. To associate a schema with the current buffer manually instead, use C-c C-s C-f (rng-set-schema-file-name).

  6. Open a DITA XML content file (.dita) with Emacs. The DITA XML syntax is shown in color. Places where the schema is not respected are underlined in red.

  7. To insert a new tag, enter <, then press Ctrl+Enter. The list of possible tags appears.

  8. Select a tag, then press Enter. Press Ctrl+Enter to display the list of permitted attributes.

  9. To insert a closing tag after text, enter </, then press Ctrl+Enter.

Speed up your typing with Predictive mode for Emacs

Section titled “Speed up your typing with Predictive mode for Emacs”

This Predictive mode tutorial for Emacs is designed to help you set up and use Emacs’ Predictive word autocompletion and editing mode in a GNU/Linux environment (in this case, Debian).

  1. Install make and texinfo:

    Terminal window
    sudo apt install make texinfo
  2. Download Predictive.

  3. Unzip the Predictive archive:

    Terminal window
    tar xzvf predictive-0.23.13.tar.gz
  4. Go to the predictive directory:

    Terminal window
    cd predictive
  5. Compile predictive:

    Terminal window
    make
  6. Install predictive:

    Terminal window
    sudo make install
  7. Insert the following code in the .emacs file:

    ;; predictive install location
    (add-to-list 'load-path "~/.emacs.d/predictive/")
    ;; dictionary locations
    (add-to-list 'load-path "~/.emacs.d/predictive/latex/")
    (add-to-list 'load-path "~/.emacs.d/predictive/texinfo/")
    (add-to-list 'load-path "~/.emacs.d/predictive/html/")
    ;; load predictive package
    (require 'predictive)
  8. Start Emacs, then press Alt+X and enter:

    predictive-mode